summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMäättä Antti <antti.maatta@qt.io>2019-02-05 15:31:46 +0200
committerAntti Määttä <antti.maatta@qt.io>2019-03-12 12:11:08 +0000
commitdb433e6b7b543a54865c770fd4ca15160e44b2c7 (patch)
tree68fb6c56f7d6b2fee91975733ee94c06698eeae0 /src
parent63ec9f17ed1cf4da27015244f97bb161e51949ce (diff)
Implement delayed image loading and slide resource list
Task-number: QT3DS-2664 Change-Id: I5310329fbf6e4e07cade28ae68161081ddf77f3c Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/runtime/q3dsengine.cpp44
-rw-r--r--src/runtime/q3dsengine_p.h65
-rw-r--r--src/runtime/q3dsimagemanager.cpp378
-rw-r--r--src/runtime/q3dsimagemanager_p.h108
-rw-r--r--src/runtime/q3dsprofiler.cpp16
-rw-r--r--src/runtime/q3dsprofiler_p.h1
-rw-r--r--src/runtime/q3dsscenemanager.cpp638
-rw-r--r--src/runtime/q3dsscenemanager_p.h16
-rw-r--r--src/runtime/q3dsslideplayer.cpp36
-rw-r--r--src/runtime/q3dsuippresentation.cpp48
-rw-r--r--src/runtime/q3dsuippresentation_p.h39
-rw-r--r--src/runtime/slideplayerng/q3dsslideplayerng.cpp35
-rw-r--r--src/runtime/slideplayerng/q3dsslideplayerng_p.h1
13 files changed, 1030 insertions, 395 deletions
diff --git a/src/runtime/q3dsengine.cpp b/src/runtime/q3dsengine.cpp
index 005ad7e..0ac262c 100644
--- a/src/runtime/q3dsengine.cpp
+++ b/src/runtime/q3dsengine.cpp
@@ -146,6 +146,27 @@ static void q3ds_msg_handler(QtMsgType type, const QMessageLogContext &ctx, cons
}
}
+void Q3DSEngine::SlideResourceCounter::print()
+{
+ static const bool debugging = qEnvironmentVariableIntValue("Q3DS_DEBUG") >= 1;
+ if (debugging) {
+ qDebug()<< "SlideResourceCounter resources:";
+ const auto keys = counters.keys();
+ for (auto &x : keys)
+ qDebug()<< x << ": " << counters[x];
+ if (createSet.size()) {
+ qDebug() << "New resources: ";
+ for (auto y : qAsConst(createSet))
+ qDebug() << y;
+ }
+ if (deleteSet.size()) {
+ qDebug() << "Deleted resources: ";
+ for (auto y : qAsConst(deleteSet))
+ qDebug() << y;
+ }
+ }
+}
+
Q3DSEngine::Q3DSEngine()
{
initResources();
@@ -920,6 +941,21 @@ void Q3DSEngine::finishAsyncLoad(bool wait)
}
}
+void Q3DSEngine::loadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+{
+ m_resourceReferenceCounter.handleLoadSlide(slide, presentation);
+ Q3DSImageManager::instance().beginImageLoad(m_resourceReferenceCounter.createSet);
+}
+
+void Q3DSEngine::unloadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+{
+ slide->setUnloadSlide(true);
+ if (!slide->active()) {
+ m_resourceReferenceCounter.handleUnloadSlide(slide, presentation);
+ Q3DSImageManager::instance().beginUnload(m_resourceReferenceCounter.deleteSet);
+ }
+}
+
bool Q3DSEngine::loadSubUipPresentation(UipPresentation *pres)
{
Q_ASSERT(pres);
@@ -1848,16 +1884,12 @@ void Q3DSEngine::goToSlideByDirection(Q3DSGraphObject *context, Q3DSUipPresentat
void Q3DSEngine::preloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
{
- Q_UNUSED(slide);
- Q_UNUSED(presentation);
- // TODO: Implementation
+ loadSlideResources(slide, presentation);
}
void Q3DSEngine::unloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
{
- Q_UNUSED(slide);
- Q_UNUSED(presentation);
- // TODO: Implementation
+ unloadSlideResources(slide, presentation);
}
void Q3DSEngine::loadBehaviorInstance(Q3DSBehaviorInstance *behaviorInstance,
diff --git a/src/runtime/q3dsengine_p.h b/src/runtime/q3dsengine_p.h
index ab85fc2..baf058b 100644
--- a/src/runtime/q3dsengine_p.h
+++ b/src/runtime/q3dsengine_p.h
@@ -270,7 +270,8 @@ public:
}
void finishAsyncLoad(bool wait = true);
-
+ void loadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation);
+ void unloadSlideResources(Q3DSSlide *slide, Q3DSUipPresentation *presentation);
void createAspectEngine();
public Q_SLOTS:
@@ -335,6 +336,67 @@ private:
void finishAsyncLoadForSubpresentation(const QString &name);
void reparentScene(SceneLoaderAsync *loader);
+ struct SlideResourceCounter
+ {
+ QHash<QUrl, int> counters;
+ QSet<QUrl> createSet;
+ QSet<QUrl> deleteSet;
+
+ QVector<Q3DSSlide *> loadedSlides;
+
+ void increment(const QSet<QUrl> &set)
+ {
+ for (auto &r : set) {
+ if (counters.value(r, 0) == 0)
+ createSet.insert(r);
+ counters[r]++;
+ }
+ }
+ void decrement(const QSet<QUrl> &set)
+ {
+ for (auto &r : set) {
+ if (counters.contains(r)) {
+ int count = qMax(counters[r] - 1, 0);
+ if (count == 0)
+ deleteSet.insert(r);
+ counters[r] = count;
+ }
+ }
+ }
+ void begin()
+ {
+ createSet.clear();
+ deleteSet.clear();
+ }
+ void reset()
+ {
+ loadedSlides.clear();
+ counters.clear();
+ begin();
+ }
+ void handleLoadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+ {
+ if (loadedSlides.contains(slide))
+ return;
+ slide->generateResourceSet(presentation);
+ loadedSlides.push_back(slide);
+ begin();
+ increment(slide->resourceSet());
+ print();
+ }
+ void handleUnloadSlide(Q3DSSlide *slide, Q3DSUipPresentation *presentation)
+ {
+ if (!loadedSlides.contains(slide))
+ return;
+ slide->generateResourceSet(presentation);
+ loadedSlides.removeOne(slide);
+ begin();
+ decrement(slide->resourceSet());
+ print();
+ }
+ void print();
+ };
+
bool loadPresentations();
void finalizePresentations();
bool loadUipPresentation(UipPresentation *pres);
@@ -405,6 +467,7 @@ private:
int m_quickDeltaCount = 0;
QVector<SceneLoaderAsync *> m_asyncSceneLoaders;
+ SlideResourceCounter m_resourceReferenceCounter;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSEngine::Flags)
diff --git a/src/runtime/q3dsimagemanager.cpp b/src/runtime/q3dsimagemanager.cpp
index f0eba05..5168e8a 100644
--- a/src/runtime/q3dsimagemanager.cpp
+++ b/src/runtime/q3dsimagemanager.cpp
@@ -45,6 +45,18 @@ QT_BEGIN_NAMESPACE
QMutex Q3DSImageManager::s_loadMutex;
+static bool isAsyncImageLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 1) > 0;
+ return enabled;
+}
+
+static bool isDelayedImageLoadingEnabled()
+{
+ static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 2) > 0;
+ return enabled;
+}
+
Q3DSImageManager::Q3DSImageManager()
{
m_threadPool.setMaxThreadCount(qMax(2, QThread::idealThreadCount() - 2));
@@ -62,13 +74,18 @@ void Q3DSImageManager::invalidate()
m_cache.clear();
m_ioTime = 0;
m_iblTime = 0;
+ m_resourceSet.clear();
+ m_loadImageDataAsync.clear();
+ qDeleteAll(m_pendingSetSource);
+ m_pendingSetSource.clear();
+ m_reloadableTextures.clear();
}
Qt3DRender::QAbstractTexture *Q3DSImageManager::newTextureForImage(Qt3DCore::QEntity *parent,
ImageFlags flags,
const QByteArray &id,
Q3DSProfiler *profiler,
- const char *profDesc, ...)
+ const QString &profilerInfo)
{
auto tex = new Qt3DRender::QTexture2D(parent);
@@ -76,14 +93,87 @@ Qt3DRender::QAbstractTexture *Q3DSImageManager::newTextureForImage(Qt3DCore::QEn
info.flags = flags;
m_metadata.insert(tex, info);
+ if (profiler && !profilerInfo.isNull())
+ profiler->trackNewObject(tex, Q3DSProfiler::Texture2DObject, id, profilerInfo);
+
+ return tex;
+}
+
+ReloadableTexturePtr Q3DSImageManager::newReloadableTextureForImage(Qt3DCore::QEntity *parent,
+ ImageFlags flags,
+ const QByteArray &id,
+ Q3DSProfiler *profiler,
+ const char *profDesc, ...)
+{
+ ReloadableTexturePtr texture;
+ QString info;
if (profiler && profDesc) {
va_list ap;
va_start(ap, profDesc);
- profiler->vtrackNewObject(tex, Q3DSProfiler::Texture2DObject, id, profDesc, ap);
+ info = QString::vasprintf(profDesc, ap);
va_end(ap);
}
+ texture.reset(new ReloadableTexture(parent, flags & GenerateMipMapsForIBL, id, info, profiler));
+ m_reloadableTextures.push_back(texture);
+ return texture;
+}
- return tex;
+void ReloadableTexture::setSource(const QUrl &source)
+{
+ if (m_source.isEmpty() || m_source != source) {
+ m_source = source;
+ m_loaded = false;
+ triggerLoading();
+ }
+}
+
+void ReloadableTexture::triggerLoading()
+{
+ if (!m_source.isEmpty() && m_loadFunc && m_unloadFunc)
+ reload();
+}
+
+void ReloadableTexture::reload()
+{
+ if (m_loaded)
+ return;
+ if (isDelayedImageLoadingEnabled() && !Q3DSImageManager::instance().inResourceSet(m_source))
+ return;
+ Q3DSImageManager::ImageFlags flags;
+ if (m_generateIBL)
+ flags = Q3DSImageManager::GenerateMipMapsForIBL;
+ if (!m_texture) {
+ m_texture = Q3DSImageManager::instance().newTextureForImage(m_parent, flags, m_id,
+ m_profiler, m_profileInfo);
+ }
+ Q3DSImageManager::instance().loadImageData(m_source, true);
+ Q3DSImageManager::instance().setSource(m_texture, m_source);
+}
+
+void ReloadableTexture::unload()
+{
+ if (m_texture) {
+ m_unloadFunc();
+
+ const auto texImages = m_texture->textureImages();
+ for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
+ m_texture->removeTextureImage(oldImage);
+ delete oldImage;
+ }
+ Qt3DCore::QNode *nullParent = nullptr;
+ m_texture->setParent(nullParent);
+ delete m_texture;
+ m_texture = nullptr;
+ m_loaded = false;
+ }
+}
+
+void ReloadableTexture::loaded(Qt3DRender::QAbstractTexture *texture)
+{
+ if (m_texture == texture) {
+ m_loaded = true;
+ m_loadFunc();
+ }
}
class Q3DSTextureImageDataGen : public Qt3DRender::QTextureImageDataGenerator
@@ -129,20 +219,18 @@ private:
Qt3DRender::QTextureImageDataGeneratorPtr m_gen;
};
-QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &source, ImageFlags flags, bool *wasCached)
+QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &source,
+ ImageFlags flags)
{
const QString sourceStr = source.toLocalFile();
s_loadMutex.lock();
auto it = m_cache.constFind(sourceStr);
if (it != m_cache.constEnd()) {
- *wasCached = true;
s_loadMutex.unlock();
return *it;
}
s_loadMutex.unlock();
- *wasCached = false;
-
QElapsedTimer t;
t.start();
qCDebug(lcScene, "Loading image %s", qPrintable(sourceStr));
@@ -260,155 +348,211 @@ QVector<Qt3DRender::QTextureImageDataPtr> Q3DSImageManager::load(const QUrl &sou
void Q3DSImageManager::finishAsyncLoad(bool wait)
{
- if (m_setSourceAsync.isEmpty())
+ QMutexLocker lock(&m_finishAsyncLoadLock);
+ if (m_loadImageDataAsync.isEmpty() && m_pendingSetSource.isEmpty())
return;
if (wait) {
+ lock.unlock();
QElapsedTimer t;
t.start();
{
QFutureSynchronizer<void> sync;
- for (auto &item : qAsConst(m_setSourceAsync))
+ for (auto &item : qAsConst(m_loadImageDataAsync))
sync.addFuture(item.future);
}
qCDebug(lcPerf, "Finish async image loading took %lld ms", t.elapsed());
- }
- bool allDone = true;
- for (int i = 0; i < m_setSourceAsync.size(); ++i) {
- auto &item = m_setSourceAsync[i];
- if (!item.done) {
- if (item.future.isFinished() && item.tex->thread() == QThread::currentThread()) {
+ for (auto &item : qAsConst(m_loadImageDataAsync)) {
+ if (m_pendingSetSource.contains(item.source)) {
+ const auto *pending = m_pendingSetSource[item.source];
+ for (const auto &tex : qAsConst(*pending))
+ setSource(tex, QUrl::fromLocalFile(item.source));
+ delete pending;
+ m_pendingSetSource.remove(item.source);
+ }
+ }
+ m_pendingSetSource.clear();
+ } else {
+ lock.relock();
+ QThread *thread = QThread::currentThread();
+ for (auto &item : m_loadImageDataAsync) {
+ if (!item.done && item.future.isFinished()) {
item.done = true;
- setSource(item.tex, item.source, item.preferKtx, false);
- } else {
- allDone = false;
+ if (m_pendingSetSource.contains(item.source)) {
+ const auto pending = *m_pendingSetSource[item.source];
+ for (const auto &tex : pending) {
+ if (tex->thread() == thread) {
+ setSource(tex, QUrl::fromLocalFile(item.source));
+ m_pendingSetSource[item.source]->removeOne(tex);
+ }
+ }
+ if (m_pendingSetSource[item.source]->empty()) {
+ delete m_pendingSetSource[item.source];
+ m_pendingSetSource.remove(item.source);
+ }
+ }
}
}
}
- if (allDone) {
- m_setSourceAsync.clear();
- m_loadImageAsync.clear();
+}
+
+void Q3DSImageManager::beginImageLoad(const QSet<QUrl> &imageSet)
+{
+ m_resourceSet.unite(imageSet);
+ for (auto url : imageSet) {
+ QFileInfo info(url.toLocalFile());
+ if (info.exists()) {
+ if (!m_loadImageDataAsync.contains(url.toLocalFile()))
+ loadImageData(url, true);
+ for (int i = 0; i < m_reloadableTextures.size(); ++i) {
+ if (m_reloadableTextures[i]->source() == url)
+ m_reloadableTextures[i]->reload();
+ }
+ }
}
}
-static bool isAsyncImageLoadingEnabled()
+bool Q3DSImageManager::inResourceSet(const QUrl &url) const
{
- static bool enabled = (qEnvironmentVariableIntValue("Q3DS_ASYNC_LOADING") & 1) > 0;
- return enabled;
+ return m_resourceSet.contains(url);
}
-void Q3DSImageManager::setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source,
- bool preferKtx, bool async)
+void Q3DSImageManager::beginUnload(const QSet<QUrl> &imageSet)
+{
+ m_resourceSet.subtract(imageSet);
+ if (!isDelayedImageLoadingEnabled())
+ return;
+ for (auto url : imageSet) {
+ for (int i = 0; i < m_reloadableTextures.size(); ++i) {
+ if (m_reloadableTextures[i]->source() == url) {
+ m_metadata.remove(m_reloadableTextures[i]->texture());
+ m_reloadableTextures[i]->unload();
+ }
+ }
+ const QString name = url.toLocalFile();
+ m_cache.remove(name);
+ if (m_loadImageDataAsync.contains(name) && m_loadImageDataAsync[name].done)
+ m_loadImageDataAsync.remove(name);
+ }
+}
+
+void Q3DSImageManager::loadImageData(const QUrl &source, bool async)
{
TextureInfo info;
- async = async && isAsyncImageLoadingEnabled();
+ auto loadImage = [this](const TextureInfo &info, const QUrl &source) {
+ QVector<Qt3DRender::QTextureImageDataPtr> imageData;
+ // The generator (invoked from some Qt3D job thread later on) will just return the already
+ // loaded data.
+ imageData = load(source, info.flags);
+ };
+
+ QMutexLocker lock(&s_loadMutex);
+ if (m_cache.contains(source.toLocalFile()))
+ return;
+ lock.unlock();
+
+ const QString src = source.toLocalFile();
+ if (async && isAsyncImageLoadingEnabled()) {
+ if (!m_loadImageDataAsync.contains(src)) {
+ qCDebug(lcScene, "Load image data async %s", qPrintable(src));
+ LoadImageDataAsync item;
+ item.source = src;
+ item.future = QtConcurrent::run(&m_threadPool, loadImage, info, source);
+ m_loadImageDataAsync[src] = item;
+ }
+ } else {
+ loadImage(info, source);
+ }
+}
+
+void Q3DSImageManager::setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source)
+{
+ TextureInfo info;
+ QString src = source.toLocalFile();
+
s_loadMutex.lock();
auto it = m_metadata.find(tex);
if (it != m_metadata.end()) {
- if (it->source == source) {
+ if (it->source == src) {
s_loadMutex.unlock();
return;
}
info = *it;
}
- s_loadMutex.unlock();
-
- auto loadImageData = [this](TextureInfo info, Qt3DRender::QAbstractTexture *tex,
- const QUrl &source, bool preferKtx, bool async) {
- QVector<Qt3DRender::QTextureImageDataPtr> imageData;
- if (!preferKtx) {
- info.source = source;
- // yes, it's all synchronous and this is intentional. The generator
- // (invoked from some Qt3D job thread later on) will just return the already
- // loaded data.
- imageData = load(source, info.flags, &info.wasCached);
- } else {
- QString ktxSource = source.toLocalFile();
- ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.')));
- ktxSource.append(QLatin1String(".ktx"));
- info.source = QUrl::fromLocalFile(ktxSource);
- imageData = load(info.source, info.flags, &info.wasCached);
- // If ktx is not found, load with the original extension
- if (imageData.isEmpty()) {
- qCWarning(lcPerf, "You have specified \"Use ktx texture if available\" option, "
- "but did not provide a ktx texture: %s", qPrintable(ktxSource));
- info.source = source;
- load(source, info.flags, &info.wasCached);
- }
- }
-
- // The rest will be done when the asynchronous loading has finished by calling this
- // function with async = false.
- if (async)
- return;
+ if (!m_cache.contains(src)) {
+ if (!m_pendingSetSource.contains(src))
+ m_pendingSetSource[src] = new QVector<Qt3DRender::QAbstractTexture *>();
+ m_pendingSetSource[src]->push_back(tex);
+ s_loadMutex.unlock();
+ return;
+ }
+ else {
+ info.wasCached = true;
+ }
+ s_loadMutex.unlock();
- const auto texImages = tex->textureImages();
- for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
- tex->removeTextureImage(oldImage);
- delete oldImage;
- }
+ auto &imageData = m_cache[src];
- if (!imageData.isEmpty()) {
- info.size = QSize(imageData[0]->width(), imageData[0]->height());
- info.format = Qt3DRender::QAbstractTexture::TextureFormat(imageData[0]->format());
- s_loadMutex.lock();
- m_metadata.insert(tex, info);
- s_loadMutex.unlock();
+ const auto texImages = tex->textureImages();
+ for (Qt3DRender::QAbstractTextureImage *oldImage : texImages) {
+ tex->removeTextureImage(oldImage);
+ delete oldImage;
+ }
- // Mipmaps are used in three cases: in IBL images (with our own custom
- // mipmap images), when the source provides mipmaps (e.g. a .ktx file
- // with mipmaps in it), or when the custom property metadata (custom
- // materials/effects) says so (mipmaps autogenerated in this case). If
- // none of these holds, linear filtering is used.
-
- tex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
- tex->setGenerateMipMaps(false); // autogen is only for custom props and that's handled elsewhere
-
- if (imageData.count() > 1) {
- tex->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
- if (!info.wasCached)
- qCDebug(lcScene, "%s provided mipmaps, mipmap filtering enabled", qPrintable(source.toLocalFile()));
- } else {
- tex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ if (!imageData.isEmpty()) {
+ info.size = QSize(imageData[0]->width(), imageData[0]->height());
+ info.format = Qt3DRender::QAbstractTexture::TextureFormat(imageData[0]->format());
+ s_loadMutex.lock();
+ m_metadata.insert(tex, info);
+ s_loadMutex.unlock();
+ // Mipmaps are used in three cases: in IBL images (with our own custom
+ // mipmap images), when the source provides mipmaps (e.g. a .ktx file
+ // with mipmaps in it), or when the custom property metadata (custom
+ // materials/effects) says so (mipmaps autogenerated in this case). If
+ // none of these holds, linear filtering is used.
+
+ tex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ // autogen is only for custom props and that's handled elsewhere
+ tex->setGenerateMipMaps(false);
+
+ if (imageData.count() > 1) {
+ tex->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
+ if (!info.wasCached) {
+ qCDebug(lcScene, "%s provided mipmaps, mipmap filtering enabled",
+ qPrintable(source.toLocalFile()));
}
-
- for (int i = 0; i < imageData.count(); ++i)
- tex->addTextureImage(new TextureImage(source, i, imageData[i]));
} else {
- // Provide a dummy image when failing to load since we want to see
- // something that makes it obvious a texture source file was missing.
- info.size = QSize(64, 64);
- info.format = Qt3DRender::QAbstractTexture::RGBA8_UNorm;
- s_loadMutex.lock();
- m_metadata.insert(tex, info);
- s_loadMutex.unlock();
-
- QImage dummy(info.size, QImage::Format_ARGB32);
- dummy.fill(Qt::magenta);
- auto dummyData = Qt3DRender::QTextureImageDataPtr::create();
- dummyData->setImage(dummy);
-
- tex->addTextureImage(new TextureImage(source, 0, dummyData));
- qWarning("Using placeholder texture in place of %s", qPrintable(source.toLocalFile()));
+ tex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
}
- };
- const QString src = source.toLocalFile();
- if (async) {
- m_setSourceAsync.append(TextureImageAsyncLoad());
- auto &item = m_setSourceAsync.last();
- item.tex = tex;
- item.source = source;
- item.preferKtx = preferKtx;
- if (!m_loadImageAsync.contains(src)) {
- qCDebug(lcScene, "Load image data async %s", qPrintable(src));
- m_loadImageAsync[src] = true;
- item.future = QtConcurrent::run(&m_threadPool, loadImageData, info, tex,
- source, preferKtx, true);
- }
+ for (int i = 0; i < imageData.count(); ++i)
+ tex->addTextureImage(new TextureImage(source, i, imageData[i]));
} else {
- loadImageData(info, tex, source, preferKtx, false);
+ // Provide a dummy image when failing to load since we want to see
+ // something that makes it obvious a texture source file was missing.
+ info.size = QSize(64, 64);
+ info.format = Qt3DRender::QAbstractTexture::RGBA8_UNorm;
+ s_loadMutex.lock();
+ m_metadata.insert(tex, info);
+ s_loadMutex.unlock();
+
+ QImage dummy(info.size, QImage::Format_ARGB32);
+ dummy.fill(Qt::magenta);
+ auto dummyData = Qt3DRender::QTextureImageDataPtr::create();
+ dummyData->setImage(dummy);
+
+ tex->addTextureImage(new TextureImage(source, 0, dummyData));
+ qWarning("Using placeholder texture in place of %s", qPrintable(source.toLocalFile()));
+ }
+ textureLoaded(tex, source);
+}
+
+void Q3DSImageManager::textureLoaded(Qt3DRender::QAbstractTexture *tex, const QUrl &source)
+{
+ for (auto &reloadable : m_reloadableTextures) {
+ if (reloadable->source() == source)
+ reloadable->loaded(tex);
}
}
diff --git a/src/runtime/q3dsimagemanager_p.h b/src/runtime/q3dsimagemanager_p.h
index 765e01e..2541224 100644
--- a/src/runtime/q3dsimagemanager_p.h
+++ b/src/runtime/q3dsimagemanager_p.h
@@ -50,6 +50,7 @@
#include <QThreadPool>
#include <Qt3DRender/QAbstractTexture>
#include <Qt3DRender/QTextureImageData>
+#include <Qt3DRender/QParameter>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,81 @@ namespace Qt3DCore {
class QEntity;
}
+class ReloadableTexture
+{
+public:
+ ReloadableTexture(Qt3DCore::QEntity *parent, bool generateIBL, const QByteArray &id,
+ const QString &profileInfo, Q3DSProfiler *profiler)
+ : m_parent(parent), m_id(id), m_profileInfo(profileInfo), m_generateIBL(generateIBL),
+ m_profiler(profiler)
+ {
+
+ }
+ void setSource(const QUrl &source);
+
+ template <typename Caller, typename... Params>
+ void onLoad(Caller caller, Params... params)
+ {
+ m_loadFunc = [this, caller, params...]() {
+ caller(m_texture, params...);
+ };
+ triggerLoading();
+ }
+
+ template <typename Caller, typename... Params>
+ void onUnload(Caller caller, Params... params)
+ {
+ m_unloadFunc = [this, caller, params...]() {
+ caller(m_texture, params...);
+ };
+ triggerLoading();
+ }
+
+ void reload();
+ void unload();
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+ void loaded(Qt3DRender::QAbstractTexture *texture);
+ bool wasLoaded() const
+ {
+ return m_loaded;
+ }
+ Qt3DRender::QAbstractTexture *texture()
+ {
+ return m_texture;
+ }
+
+ QString info()
+ {
+ return m_profileInfo;
+ }
+
+ Q3DSProfiler *profiler()
+ {
+ return m_profiler;
+ }
+
+private:
+ void triggerLoading();
+
+ Qt3DCore::QEntity *m_parent = nullptr;
+ Qt3DRender::QAbstractTexture *m_texture = nullptr;
+ QByteArray m_id;
+ QString m_profileInfo;
+ QUrl m_source;
+ bool m_generateIBL = false;
+ bool m_loaded = false;
+ Q3DSProfiler *m_profiler = nullptr;
+
+ std::function<void()> m_loadFunc = nullptr;
+ std::function<void()> m_unloadFunc = nullptr;
+};
+
+typedef QSharedPointer<ReloadableTexture> ReloadableTexturePtr;
+
class Q3DSImageManager
{
public:
@@ -76,9 +152,13 @@ public:
ImageFlags flags,
const QByteArray &id,
Q3DSProfiler *profiler = nullptr,
- const char *profName = nullptr, ...);
- void setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source, bool preferKtx,
- bool async = true);
+ const QString &profilerInfo = {});
+ ReloadableTexturePtr newReloadableTextureForImage(Qt3DCore::QEntity *parent,
+ ImageFlags flags,
+ const QByteArray &id,
+ Q3DSProfiler *profiler = nullptr,
+ const char *profName = nullptr, ...);
+
void setSource(Qt3DRender::QAbstractTexture *tex, const QImage &image);
QSize size(Qt3DRender::QAbstractTexture *tex) const;
@@ -89,14 +169,21 @@ public:
qint64 iblTimeMsecs() const { return m_iblTime; }
void finishAsyncLoad(bool wait = true);
+ void beginImageLoad(const QSet<QUrl> &imageSet);
+ void beginUnload(const QSet<QUrl> &imageSet);
+ bool inResourceSet(const QUrl &url) const;
private:
- QVector<Qt3DRender::QTextureImageDataPtr> load(const QUrl &source, ImageFlags flags, bool *wasCached);
+ void loadImageData(const QUrl &source, bool async = true);
+ void setSource(Qt3DRender::QAbstractTexture *tex, const QUrl &source);
+ void textureLoaded(Qt3DRender::QAbstractTexture *tex, const QUrl &source);
+ QVector<Qt3DRender::QTextureImageDataPtr> load(const QUrl &source, ImageFlags flags);
int blockSizeForFormat(QOpenGLTexture::TextureFormat format);
QByteArray generateIblMip(int w, int h, int prevW, int prevH,
QOpenGLTexture::TextureFormat format,
int blockSize, const QByteArray &prevLevelData);
+
struct TextureInfo {
ImageFlags flags;
QUrl source;
@@ -105,23 +192,26 @@ private:
bool wasCached = false;
};
- struct TextureImageAsyncLoad
+ struct LoadImageDataAsync
{
QFuture<void> future;
- Qt3DRender::QAbstractTexture *tex = nullptr;
- QUrl source;
+ QString source;
bool preferKtx = false;
bool done = false;
};
+ QVector<ReloadableTexturePtr> m_reloadableTextures;
+ QSet<QUrl> m_resourceSet;
QHash<Qt3DRender::QAbstractTexture *, TextureInfo> m_metadata;
QHash<QString, QVector<Qt3DRender::QTextureImageDataPtr> > m_cache;
- QHash<QString, bool> m_loadImageAsync;
- QVector<TextureImageAsyncLoad> m_setSourceAsync;
+ QHash<QString, LoadImageDataAsync> m_loadImageDataAsync;
+ QHash<QString, QVector<Qt3DRender::QAbstractTexture *> *> m_pendingSetSource;
QThreadPool m_threadPool;
static QMutex s_loadMutex;
qint64 m_ioTime = 0;
qint64 m_iblTime = 0;
+ QMutex m_finishAsyncLoadLock;
+ friend ReloadableTexture;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSImageManager::ImageFlags)
diff --git a/src/runtime/q3dsprofiler.cpp b/src/runtime/q3dsprofiler.cpp
index 727229a..1a02084 100644
--- a/src/runtime/q3dsprofiler.cpp
+++ b/src/runtime/q3dsprofiler.cpp
@@ -121,6 +121,22 @@ void Q3DSProfiler::trackNewObject(QObject *obj, ObjectType type, const QByteArra
va_end(ap);
}
+void Q3DSProfiler::trackNewObject(QObject *obj, ObjectType type, const QByteArray &id,
+ const QString &info)
+{
+ if (!m_enabled)
+ return;
+
+ ObjectData objd(obj, type, id);
+ objd.info = info;
+ m_objectData.insert(type, objd);
+
+ m_objectDestroyConnections.append(QObject::connect(obj, &QObject::destroyed,
+ [this, obj, type, id]() {
+ m_objectData.remove(type, ObjectData(obj, type, id));
+ }));
+}
+
void Q3DSProfiler::vtrackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, va_list args)
{
if (!m_enabled)
diff --git a/src/runtime/q3dsprofiler_p.h b/src/runtime/q3dsprofiler_p.h
index 7a65f45..2bc391c 100644
--- a/src/runtime/q3dsprofiler_p.h
+++ b/src/runtime/q3dsprofiler_p.h
@@ -89,6 +89,7 @@ public:
ShaderProgramObject
};
void trackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, ...);
+ void trackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const QString &info);
void vtrackNewObject(QObject *obj, ObjectType type, const QByteArray &id, const char *info, va_list args);
void updateObjectInfo(QObject *obj, ObjectType type, const QByteArray &id, const char *info, ...);
diff --git a/src/runtime/q3dsscenemanager.cpp b/src/runtime/q3dsscenemanager.cpp
index 060b43c..391573b 100644
--- a/src/runtime/q3dsscenemanager.cpp
+++ b/src/runtime/q3dsscenemanager.cpp
@@ -47,6 +47,7 @@
#include "q3dslogging_p.h"
#include "q3dsviewportsettings_p.h"
#include "q3dstextmesh_p.h"
+#include "q3dsimagemanager_p.h"
#if QT_CONFIG(q3ds_profileui)
#include "profileui/q3dsprofileui_p.h"
#include "q3dsconsolecommands_p.h"
@@ -859,14 +860,19 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
// Do some pre-processing and early setup for some objects.
initSubTree(m_scene);
+ m_engine->loadSlideResources(m_masterSlide, m_presentation);
+ m_engine->loadSlideResources(m_currentSlide, m_presentation);
+
// Build the (offscreen) Qt3D scene
m_layerContainerFg = new Qt3DRender::QFrameGraphNode(frameGraphRoot);
new Qt3DRender::QNoDraw(m_layerContainerFg); // in case there are no layers at all
+
Q3DSUipPresentation::forAllLayers(m_scene, [=](Q3DSLayerNode *layer3DS) {
if (layer3DS->sourcePath().isEmpty())
buildLayer(layer3DS, m_layerContainerFg, m_outputPixelSize);
else
buildSubPresentationLayer(layer3DS, m_outputPixelSize);
+
});
// The Scene object may have non-layer children.
@@ -879,7 +885,6 @@ Q3DSSceneManager::Scene Q3DSSceneManager::buildScene(Q3DSUipPresentation *presen
// Onscreen (or not) compositor (still offscreen when this is a subpresentation)
buildCompositor(frameGraphRoot, m_rootEntity);
-
// Profiling UI (main presentation only)
#if QT_CONFIG(q3ds_profileui)
if (!m_flags.testFlag(SubPresentation)) {
@@ -2181,7 +2186,7 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
if (layer3DS->lightProbe()) {
// initialize light probe parameters if necessary
if (!data->iblProbeData.lightProbeTexture) {
- data->iblProbeData.lightProbeTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->iblProbeData.lightProbeTexture = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
layer3DS->lightProbe()->id(),
m_profiler, "iblProbe texture for image %s", layer3DS->lightProbe()->id().constData());
@@ -2204,59 +2209,75 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
// Update light probe parameter values
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->iblProbeData.lightProbeTexture,
- QUrl::fromLocalFile(layer3DS->lightProbe()->sourcePath()),
- m_presentation->preferKtx());
- data->iblProbeData.lightProbeSampler->setValue(QVariant::fromValue(data->iblProbeData.lightProbeTexture));
+ data->iblProbeData.lightProbeTexture->setSource(m_presentation->imageUrl(
+ layer3DS->lightProbe()->sourcePath()));
+ data->iblProbeData.lightProbeTexture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbeSampler->setValue(QVariant::fromValue(texture));
- Qt3DRender::QTextureWrapMode wrapMode;
+ Qt3DRender::QTextureWrapMode wrapMode;
- switch (layer3DS->lightProbe()->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (layer3DS->lightProbe()->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- switch (layer3DS->lightProbe()->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (layer3DS->lightProbe()->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- Qt3DRender::QAbstractTexture *texture = data->iblProbeData.lightProbeTexture;
- texture->setWrapMode(wrapMode);
+ texture->setWrapMode(wrapMode);
- const QMatrix4x4 &textureTransform = layer3DS->lightProbe()->textureTransform();
- const float *m = textureTransform.constData();
+ const QMatrix4x4 &textureTransform = layer3DS->lightProbe()->textureTransform();
+ const float *m = textureTransform.constData();
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(texture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->iblProbeData.lightProbeOffset->setValue(offsets);
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->iblProbeData.lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->iblProbeData.lightProbeRotation->setValue(rotations);
+ }, layer3DS);
+
+ data->iblProbeData.lightProbeTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q_UNUSED(texture);
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbeSampler->setValue({});
+ QVector4D offsets(0.f, 0.f, 0.f, 0.f);
+ data->iblProbeData.lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(0.f, 0.f, 0.f, 0.f);
+ data->iblProbeData.lightProbeRotation->setValue(rotations);
+ }, layer3DS);
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->iblProbeData.lightProbeRotation->setValue(rotations);
if (layer3DS->lightProbe2()) {
// Initialize light probe 2 parameters
if (!data->iblProbeData.lightProbe2Texture) {
- data->iblProbeData.lightProbe2Texture = Q3DSImageManager::instance().newTextureForImage(
+ data->iblProbeData.lightProbe2Texture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
layer3DS->lightProbe2()->id(),
- m_profiler, "iblProbe2 texture for image %s", layer3DS->lightProbe2()->id().constData());
+ m_profiler, "iblProbe2 texture for image %s",
+ layer3DS->lightProbe2()->id().constData());
}
if (!data->iblProbeData.lightProbe2Sampler) {
@@ -2267,22 +2288,63 @@ void Q3DSSceneManager::setLayerProperties(Q3DSLayerNode *layer3DS)
// Update light probe 2 parameter values
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->iblProbeData.lightProbe2Texture,
- QUrl::fromLocalFile(layer3DS->lightProbe2()->sourcePath()),
- m_presentation->preferKtx());
- data->iblProbeData.lightProbe2Sampler->setValue(QVariant::fromValue(data->iblProbeData.lightProbe2Texture));
+ data->iblProbeData.lightProbe2Texture->setSource(
+ m_presentation->imageUrl(layer3DS->lightProbe2()->sourcePath()));
+ data->iblProbeData.lightProbe2Texture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSLayerNode *layer3DS) {
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbe2Sampler->setValue(QVariant::fromValue(texture));
+
+ Qt3DRender::QTextureWrapMode wrapMode;
+
+ switch (layer3DS->lightProbe()->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- data->iblProbeData.lightProbe2Texture->setWrapMode(wrapMode);
+ switch (layer3DS->lightProbe()->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- QVector4D probe2Props(layer3DS->probe2Window(), layer3DS->probe2Pos(), layer3DS->probe2Fade(), 1.0f);
- data->iblProbeData.lightProbe2Properties->setValue(probe2Props);
- const QMatrix4x4 &textureTransform = layer3DS->lightProbe2()->textureTransform();
- const float *m = textureTransform.constData();
- QVector4D probeProps(m[12], m[13], layer3DS->probeHorizon(), layer3DS->probeBright() * 0.01f);
- data->iblProbeData.lightProbeProperties->setValue(probeProps);
+ texture->setWrapMode(wrapMode);
+
+ QVector4D probe2Props(layer3DS->probe2Window(), layer3DS->probe2Pos(),
+ layer3DS->probe2Fade(), 1.0f);
+ data->iblProbeData.lightProbe2Properties->setValue(probe2Props);
+ const QMatrix4x4 &textureTransform = layer3DS->lightProbe2()->textureTransform();
+ const float *m = textureTransform.constData();
+ QVector4D probeProps(m[12], m[13], layer3DS->probeHorizon(),
+ layer3DS->probeBright() * 0.01f);
+ data->iblProbeData.lightProbeProperties->setValue(probeProps);
+ }, layer3DS);
+ data->iblProbeData.lightProbe2Texture->onUnload(
+ [](Qt3DRender::QAbstractTexture *texture, Q3DSLayerNode *layer3DS) {
+ Q_UNUSED(texture);
+ Q3DSLayerAttached *data = static_cast<Q3DSLayerAttached *>(layer3DS->attached());
+ data->iblProbeData.lightProbe2Sampler->setValue({});
+ data->iblProbeData.lightProbe2Properties->setValue({});
+ data->iblProbeData.lightProbeProperties->setValue({});
+ }, layer3DS);
} else {
data->iblProbeData.lightProbe2Properties->setValue(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
- data->iblProbeData.lightProbeProperties->setValue(QVector4D(0.0f, 0.0f, layer3DS->probeHorizon(), layer3DS->probeBright() * 0.01f));
+ data->iblProbeData.lightProbeProperties->setValue(
+ QVector4D(0.0f, 0.0f, layer3DS->probeHorizon(),
+ layer3DS->probeBright() * 0.01f));
}
}
}
@@ -5786,7 +5848,6 @@ static void addShadowSsaoParams(Q3DSLayerAttached *layerData, QVector<Qt3DRender
void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
{
// Scene building phase 2: all lights are known -> generate actual Qt3D materials
-
Q3DSModelAttached *modelData = static_cast<Q3DSModelAttached *>(model3DS->attached());
if (!modelData)
return;
@@ -5814,7 +5875,6 @@ void Q3DSSceneManager::buildModelMaterial(Q3DSModelNode *model3DS)
// matters so much.
lightNodes.resize(m_gfxLimits.maxLightsPerLayer);
}
-
for (Q3DSModelAttached::SubMesh &sm : modelData->subMeshes) {
if (sm.resolvedMaterial && !sm.materialComponent && sm.resolvedMaterial->type() != Q3DSGraphObject::ReferencedMaterial) {
Q_ASSERT(sm.resolvedMaterial->attached());
@@ -6092,15 +6152,58 @@ void Q3DSSceneManager::prepareTextureParameters(Q3DSTextureParameters &texturePa
textureParameters.size = new Qt3DRender::QParameter;
textureParameters.size->setName(name + QLatin1String("_size"));
-
- textureParameters.texture = Q3DSImageManager::instance().newTextureForImage(
- m_rootEntity, 0, image3DS->id(), m_profiler, "Texture for image %s", image3DS->id().constData());
}
void Q3DSSceneManager::updateTextureParameters(Q3DSTextureParameters &textureParameters, Q3DSImage *image)
{
// note that this function is called frequently (whenever any Image parameter changes)
+ auto setupTexture = [](Q3DSTextureParameters &textureParameters, Q3DSImage *image) {
+ Qt3DRender::QTextureWrapMode wrapMode;
+ switch (image->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+ switch (image->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ Qt3DRender::QAbstractTexture *texture
+ = textureParameters.sampler->value().value<Qt3DRender::QAbstractTexture *>();
+ Q_ASSERT(texture || !image->subPresentation().isEmpty());
+ if (!texture)
+ return;
+
+ texture->setWrapMode(wrapMode);
+ // min/mag are already set at this point
+
+ const QMatrix4x4 &textureTransform = image->textureTransform();
+ const float *m = textureTransform.constData();
+
+ QVector3D offsets(m[12], m[13], image->hasPremultipliedAlpha() ? 1 : 0);
+ textureParameters.offsets->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ textureParameters.rotations->setValue(rotations);
+ const QSize size = Q3DSImageManager::instance().size(texture);
+ textureParameters.size->setValue(QVector2D(size.width(), size.height()));
+ };
+
if (!image->subPresentation().isEmpty()) {
if (textureParameters.subPresId != image->subPresentation()) {
textureParameters.subPresId = image->subPresentation();
@@ -6112,60 +6215,40 @@ void Q3DSSceneManager::updateTextureParameters(Q3DSTextureParameters &texturePar
setImageTextureFromSubPresentation(textureParameters.sampler, image);
}
}
+ setupTexture(textureParameters, image);
} else if (!image->sourcePath().isEmpty()) {
- Q3DSImageManager::instance().setSource(textureParameters.texture, QUrl::fromLocalFile(image->sourcePath()),
- m_presentation->preferKtx());
- textureParameters.sampler->setValue(QVariant::fromValue(textureParameters.texture));
+ if (!textureParameters.textureSource) {
+ textureParameters.textureSource
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
+ m_rootEntity, 0, image->id(), m_profiler, "Texture for image %s",
+ image->id().constData());
+ }
+ textureParameters.textureSource->setSource(m_presentation->imageUrl(image->sourcePath()));
+ textureParameters.textureSource->onLoad(
+ [setupTexture](Qt3DRender::QAbstractTexture *texture,
+ Q3DSTextureParameters *param, Q3DSImage *image) {
+ param->sampler->setValue(QVariant::fromValue(texture));
+ setupTexture(*param, image);
+ }, &textureParameters, image);
+ textureParameters.textureSource->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSTextureParameters *param) {
+ Q_UNUSED(texture);
+ param->sampler->setValue({});
+ param->offsets->setValue({});
+ param->rotations->setValue({});
+ param->size->setValue({});
+ }, &textureParameters);
} else if (!image->customImage().isNull()) {
+ textureParameters.texture
+ = Q3DSImageManager::instance().newTextureForImage(m_rootEntity, 0, image->id(),
+ m_profiler, QStringLiteral("Texture for image %1").arg(image->id().constData()));
Q3DSImageManager::instance().setSource(textureParameters.texture, image->customImage());
textureParameters.sampler->setValue(QVariant::fromValue(textureParameters.texture));
+ setupTexture(textureParameters, image);
} else {
textureParameters.sampler->setValue(QVariant::fromValue(dummyTexture(image->id())));
+ setupTexture(textureParameters, image);
}
-
- Qt3DRender::QTextureWrapMode wrapMode;
- switch (image->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
- switch (image->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
-
- Qt3DRender::QAbstractTexture *texture = textureParameters.sampler->value().value<Qt3DRender::QAbstractTexture *>();
- Q_ASSERT(texture || !image->subPresentation().isEmpty());
- if (!texture)
- return;
-
- texture->setWrapMode(wrapMode);
- // min/mag are already set at this point
-
- const QMatrix4x4 &textureTransform = image->textureTransform();
- const float *m = textureTransform.constData();
-
- QVector3D offsets(m[12], m[13], image->hasPremultipliedAlpha() ? 1 : 0);
- textureParameters.offsets->setValue(offsets);
-
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- textureParameters.rotations->setValue(rotations);
-
- const QSize size = Q3DSImageManager::instance().size(texture);
- textureParameters.size->setValue(QVector2D(size.width(), size.height()));
}
void Q3DSSceneManager::setImageTextureFromSubPresentation(Qt3DRender::QParameter *sampler, Q3DSImage *image)
@@ -6438,10 +6521,11 @@ QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareDefaultMaterial(Q3DSD
iblOverrideImage = m->lightProbe();
if (iblOverrideImage) {
if (!data->lightProbeOverrideTexture) {
- data->lightProbeOverrideTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->lightProbeOverrideTexture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
- iblOverrideImage->id(),
- m_profiler, "Texture for image %s", iblOverrideImage->id().constData());
+ iblOverrideImage->id(), m_profiler,
+ "Texture for image %s", iblOverrideImage->id().constData());
data->lightProbeSampler = new Qt3DRender::QParameter;
data->lightProbeSampler->setName(QLatin1String("light_probe"));
@@ -6583,6 +6667,48 @@ void Q3DSSceneManager::updateDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferen
// IBL
if (iblOverride) {
+ auto setupTexture = [](Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data, Q3DSImage *iblOverride) {
+ Qt3DRender::QTextureWrapMode wrapMode;
+
+ switch (iblOverride->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ switch (iblOverride->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ texture->setWrapMode(wrapMode);
+
+ const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
+ const float *m = textureTransform.constData();
+
+ // offsets.w = max mip level
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->lightProbeOffset->setValue(offsets);
+
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->lightProbeRotation->setValue(rotations);
+ };
// also sets min/mag and generates mipmaps
if (!iblOverride->subPresentation().isEmpty()) {
if (m_subPresentations.isEmpty())
@@ -6590,52 +6716,23 @@ void Q3DSSceneManager::updateDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferen
else
setImageTextureFromSubPresentation(data->lightProbeSampler, iblOverride);
} else {
- Q3DSImageManager::instance().setSource(data->lightProbeOverrideTexture,
- QUrl::fromLocalFile(iblOverride->sourcePath()),
- m_presentation->preferKtx());
- data->lightProbeSampler->setValue(QVariant::fromValue(data->lightProbeOverrideTexture));
- }
-
- Qt3DRender::QTextureWrapMode wrapMode;
-
- switch (iblOverride->horizontalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
-
- switch (iblOverride->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
+ data->lightProbeOverrideTexture->setSource(
+ m_presentation->imageUrl(iblOverride->sourcePath()));
+ data->lightProbeOverrideTexture->onLoad([setupTexture](
+ Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data,
+ Q3DSImage *iblOverride) {
+ data->lightProbeSampler->setValue(QVariant::fromValue(texture));
+ setupTexture(texture, data, iblOverride);
+ }, data, iblOverride);
+ data->lightProbeOverrideTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSDefaultMaterialAttached *data) {
+ Q_UNUSED(texture);
+ data->lightProbeSampler->setValue({});
+ data->lightProbeOffset->setValue({});
+ data->lightProbeRotation->setValue({});
+ }, data);
}
-
- Q_ASSERT(data->lightProbeOverrideTexture);
- data->lightProbeOverrideTexture->setWrapMode(wrapMode);
-
- const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
- const float *m = textureTransform.constData();
-
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(data->lightProbeOverrideTexture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->lightProbeOffset->setValue(offsets);
-
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->lightProbeRotation->setValue(rotations);
}
}
@@ -6663,71 +6760,91 @@ static inline void forAllCustomProperties(Q3DSEffectInstance *eff3DS, CustomProp
iterateCustomProperties(eff3DS->dynamicProperties(), eff3DS->effect()->properties(), callback);
}
-Qt3DRender::QAbstractTexture *Q3DSSceneManager::createCustomPropertyTexture(const Q3DSCustomPropertyParameter &p, const QByteArray &id)
+void Q3DSSceneManager::createCustomPropertyTexture(Q3DSCustomPropertyParameter &p,
+ const QByteArray &id)
{
+ // now override the defaults set in setSource() with whatever the metadata specifies
+ auto setupTexture = [](Q3DSCustomPropertyParameter *p, Qt3DRender::QAbstractTexture *texture) {
+ switch (p->meta.magFilterType) {
+ case Q3DSMaterial::Nearest:
+ texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Nearest);
+ break;
+ default:
+ texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ break;
+ }
+
+ switch (p->meta.minFilterType) {
+ case Q3DSMaterial::Nearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Nearest);
+ break;
+ case Q3DSMaterial::Linear:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
+ break;
+ case Q3DSMaterial::NearestMipmapNearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapNearest);
+ texture->setGenerateMipMaps(true);
+ break;
+ case Q3DSMaterial::NearestMipmapLinear:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapLinear);
+ texture->setGenerateMipMaps(true);
+ break;
+ case Q3DSMaterial::LinearMipmapNearest:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapNearest);
+ texture->setGenerateMipMaps(true);
+ break;
+ default:
+ texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
+ texture->setGenerateMipMaps(true);
+ break;
+ }
+
+ Qt3DRender::QTextureWrapMode wrapMode;
+ switch (p->meta.clampType) {
+ case Q3DSMaterial::Repeat:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+ texture->setWrapMode(wrapMode);
+ };
+
const QString source = p.inputValue.toString();
Qt3DRender::QAbstractTexture *texture;
if (source.isEmpty()) {
texture = dummyTexture(id);
+ setupTexture(&p, texture);
+ p.param->setValue(QVariant::fromValue(texture));
} else {
- texture = Q3DSImageManager::instance().newTextureForImage(m_rootEntity, 0, id, m_profiler,
- "Custom property texture %s", qPrintable(source));
+ p.texture = Q3DSImageManager::instance().newReloadableTextureForImage(
+ m_rootEntity, 0, id, m_profiler, "Custom property texture %s",
+ qPrintable(source));
qCDebug(lcScene, "Creating custom property texture %s", qPrintable(source));
- Q3DSImageManager::instance().setSource(texture, QUrl::fromLocalFile(source),
- m_presentation->preferKtx());
- }
-
- // now override the defaults set in setSource() with whatever the metadata specifies
-
- switch (p.meta.magFilterType) {
- case Q3DSMaterial::Nearest:
- texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Nearest);
- break;
- default:
- texture->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
- break;
- }
-
- switch (p.meta.minFilterType) {
- case Q3DSMaterial::Nearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Nearest);
- break;
- case Q3DSMaterial::Linear:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
- break;
- case Q3DSMaterial::NearestMipmapNearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapNearest);
- texture->setGenerateMipMaps(true);
- break;
- case Q3DSMaterial::NearestMipmapLinear:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::NearestMipMapLinear);
- texture->setGenerateMipMaps(true);
- break;
- case Q3DSMaterial::LinearMipmapNearest:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapNearest);
- texture->setGenerateMipMaps(true);
- break;
- default:
- texture->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
- texture->setGenerateMipMaps(true);
- break;
-
- }
-
- Qt3DRender::QTextureWrapMode wrapMode;
- switch (p.meta.clampType) {
- case Q3DSMaterial::Repeat:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
+ p.texture->setSource(m_presentation->imageUrl(source));
+ p.texture->onLoad([setupTexture](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomPropertyParameter *p) {
+ p->param->setValue(QVariant::fromValue(texture));
+ if (p->texFlagParam)
+ p->texFlagParam->setValue(1);
+ const QSize size = Q3DSImageManager::instance().size(texture);
+ const bool isPremultiplied = false;
+ if (p->texInfoParam) {
+ p->texInfoParam->setValue(QVector4D(size.width(), size.height(),
+ isPremultiplied ? 1 : 0, 0));
+ }
+ setupTexture(p, texture);
+ }, &p);
+ p.texture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomPropertyParameter *p) {
+ Q_UNUSED(texture);
+ p->param->setValue({});
+ p->texInfoParam->setValue({});
+ }, &p);
}
- texture->setWrapMode(wrapMode);
-
- return texture;
}
QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS)
@@ -6819,7 +6936,8 @@ QVector<Qt3DRender::QParameter *> Q3DSSceneManager::prepareCustomMaterial(Q3DSCu
iblOverrideImage = m->lightProbe();
if (iblOverrideImage) {
if (!data->lightProbeOverrideTexture) {
- data->lightProbeOverrideTexture = Q3DSImageManager::instance().newTextureForImage(
+ data->lightProbeOverrideTexture
+ = Q3DSImageManager::instance().newReloadableTextureForImage(
m_rootEntity, Q3DSImageManager::GenerateMipMapsForIBL,
iblOverrideImage->id(),
m_profiler, "Texture for image %s", iblOverrideImage->id().constData());
@@ -6867,7 +6985,7 @@ void Q3DSSceneManager::updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSR
// point whereas we need a proper Qt 3D texture.
switch (p.meta.type) {
case Q3DS::Texture:
- p.param->setValue(QVariant::fromValue(createCustomPropertyTexture(p, m->id())));
+ createCustomPropertyTexture(p, m->id());
break;
// Buffer, Image2D, etc. are not used for custom materials
@@ -6917,52 +7035,61 @@ void Q3DSSceneManager::updateCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSR
// IBL
if (iblOverride) {
// also sets min/mag and generates mipmaps
- Q3DSImageManager::instance().setSource(data->lightProbeOverrideTexture,
- QUrl::fromLocalFile(iblOverride->sourcePath()),
- m_presentation->preferKtx());
- data->lightProbeSampler->setValue(QVariant::fromValue(data->lightProbeOverrideTexture));
-
- Qt3DRender::QTextureWrapMode wrapMode;
- wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
-
- switch (iblOverride->horizontalTiling()) {
- case Q3DSImage::Tiled:
+ data->lightProbeOverrideTexture->setSource(
+ m_presentation->imageUrl(iblOverride->sourcePath()));
+ data->lightProbeOverrideTexture->onLoad([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSImage *iblOverride,
+ Q3DSCustomMaterialAttached *data) {
+ data->lightProbeSampler->setValue(QVariant::fromValue(texture));
+
+ Qt3DRender::QTextureWrapMode wrapMode;
wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
- switch (iblOverride->verticalTiling()) {
- case Q3DSImage::Tiled:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
- break;
- case Q3DSImage::Mirrored:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
- break;
- default:
- wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
- break;
- }
+ switch (iblOverride->horizontalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
+
+ switch (iblOverride->verticalTiling()) {
+ case Q3DSImage::Tiled:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::Repeat);
+ break;
+ case Q3DSImage::Mirrored:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::MirroredRepeat);
+ break;
+ default:
+ wrapMode.setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
+ break;
+ }
- Q_ASSERT(data->lightProbeOverrideTexture);
- data->lightProbeOverrideTexture->setWrapMode(wrapMode);
+ texture->setWrapMode(wrapMode);
- const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
- const float *m = textureTransform.constData();
+ const QMatrix4x4 &textureTransform = iblOverride->textureTransform();
+ const float *m = textureTransform.constData();
- // offsets.w = max mip level
- const QSize texSize = Q3DSImageManager::instance().size(data->lightProbeOverrideTexture);
- float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
- QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
- data->lightProbeOffset->setValue(offsets);
+ // offsets.w = max mip level
+ const QSize texSize = Q3DSImageManager::instance().size(texture);
+ float mipLevels = float(qCeil(qLog2(qMax(texSize.width(), texSize.height()))));
+ QVector4D offsets(m[12], m[13], 0.0f, mipLevels);
+ data->lightProbeOffset->setValue(offsets);
- QVector4D rotations(m[0], m[4], m[1], m[5]);
- data->lightProbeRotation->setValue(rotations);
+ QVector4D rotations(m[0], m[4], m[1], m[5]);
+ data->lightProbeRotation->setValue(rotations);
+ }, iblOverride, data);
+ data->lightProbeOverrideTexture->onUnload([](Qt3DRender::QAbstractTexture *texture,
+ Q3DSCustomMaterialAttached *data) {
+ Q_UNUSED(texture);
+ data->lightProbeSampler->setValue({});
+ data->lightProbeOffset->setValue({});
+ data->lightProbeRotation->setValue({});
+ }, data);
}
}
@@ -7945,10 +8072,7 @@ void Q3DSSceneManager::updateEffect(Q3DSEffectInstance *eff3DS)
switch (p.meta.type) {
case Q3DS::Texture:
{
- Qt3DRender::QAbstractTexture *tex = createCustomPropertyTexture(p, eff3DS->id());
- p.param->setValue(QVariant::fromValue(tex));
- setTextureInfoUniform(p.texInfoParam, tex);
- p.texFlagParam->setValue(1);
+ createCustomPropertyTexture(p, eff3DS->id());
}
break;
diff --git a/src/runtime/q3dsscenemanager_p.h b/src/runtime/q3dsscenemanager_p.h
index 4a0b2c0..f8c836d 100644
--- a/src/runtime/q3dsscenemanager_p.h
+++ b/src/runtime/q3dsscenemanager_p.h
@@ -115,6 +115,9 @@ namespace Qt3DExtras {
class QPlaneMesh;
}
+class ReloadableTexture;
+typedef QSharedPointer<ReloadableTexture> ReloadableTexturePtr;
+
struct Q3DSEyeData
{
Qt3DRender::QCameraSelector *cameraSelector = nullptr;
@@ -405,8 +408,8 @@ public:
} advBlend;
struct IBLProbeData {
- Qt3DRender::QAbstractTexture *lightProbeTexture = nullptr;
- Qt3DRender::QAbstractTexture *lightProbe2Texture = nullptr;
+ ReloadableTexturePtr lightProbeTexture = nullptr;
+ ReloadableTexturePtr lightProbe2Texture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
@@ -511,6 +514,7 @@ struct Q3DSTextureParameters
QVector<Qt3DRender::QParameter *> parameters() const { return { sampler, offsets, rotations, size }; }
Qt3DRender::QAbstractTexture *texture = nullptr;
+ ReloadableTexturePtr textureSource = nullptr;
QString subPresId;
};
@@ -565,7 +569,7 @@ public:
Q3DSTextureParameters lightmapShadowParams;
// IBL
QMetaObject::Connection updateOffsetConnection;
- Qt3DRender::QAbstractTexture *lightProbeOverrideTexture = nullptr;
+ ReloadableTexturePtr lightProbeOverrideTexture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
Qt3DRender::QParameter *lightProbeOffset = nullptr;
@@ -578,6 +582,7 @@ struct Q3DSCustomPropertyParameter {
meta(meta_)
{ }
Q3DSCustomPropertyParameter() { }
+ ReloadableTexturePtr texture = nullptr;
Qt3DRender::QParameter *param = nullptr;
QVariant inputValue; // e.g. Texture: inputValue is a string whereas param->value is a QAbstractTexture*
Q3DSMaterial::PropertyElement meta;
@@ -600,7 +605,7 @@ public:
// IBL
QMetaObject::Connection updateOffsetConnection;
- Qt3DRender::QAbstractTexture *lightProbeOverrideTexture = nullptr;
+ ReloadableTexturePtr lightProbeOverrideTexture = nullptr;
Qt3DRender::QParameter *lightProbeSampler = nullptr;
Qt3DRender::QParameter *lightProbeRotation = nullptr;
Qt3DRender::QParameter *lightProbeOffset = nullptr;
@@ -807,6 +812,7 @@ public:
Q3DSInputManager *inputManager() { return m_inputManager; }
Q3DSTextRenderer *textRenderer() const { return m_textRenderer; }
Q3DSEngine *engine() const { return m_engine; }
+ Q3DSUipPresentation *presentation() const { return m_presentation; }
void setEyeDepthTextureEnabled(Q3DSLayerNode *layer3DS, Q3DSEyeData *eyeData, bool enabled);
void setDepthTextureEnabled(Q3DSLayerNode *layer3DS, bool enabled);
@@ -899,7 +905,7 @@ private:
bool checkImageTransparency(Q3DSImage *image) const;
void prepareTextureParameters(Q3DSTextureParameters &textureParameters, const QString &name, Q3DSImage *image3DS);
QVector<Qt3DRender::QParameter *> prepareDefaultMaterial(Q3DSDefaultMaterial *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS);
- Qt3DRender::QAbstractTexture *createCustomPropertyTexture(const Q3DSCustomPropertyParameter &p, const QByteArray &id);
+ void createCustomPropertyTexture(Q3DSCustomPropertyParameter &p, const QByteArray &id);
QVector<Qt3DRender::QParameter *> prepareCustomMaterial(Q3DSCustomMaterialInstance *m, Q3DSReferencedMaterial *rm, Q3DSModelNode *model3DS);
void setImageTextureFromSubPresentation(Qt3DRender::QParameter *sampler, Q3DSImage *image);
void updateTextureParameters(Q3DSTextureParameters &textureParameters, Q3DSImage *image);
diff --git a/src/runtime/q3dsslideplayer.cpp b/src/runtime/q3dsslideplayer.cpp
index 0bf850f..a2a001c 100644
--- a/src/runtime/q3dsslideplayer.cpp
+++ b/src/runtime/q3dsslideplayer.cpp
@@ -34,6 +34,7 @@
#include <QtCore/qmetaobject.h>
#include "q3dsscenemanager_p.h"
#include "q3dsanimationmanager_p.h"
+#include "q3dsengine_p.h"
#include "q3dslogging_p.h"
#include <Qt3DAnimation/qclipanimator.h>
@@ -518,6 +519,19 @@ void Q3DSSlidePlayer::setSlideDeck(Q3DSSlideDeck *slideDeck)
forAllSlides(slideDeck);
+ m_sceneManager->engine()->loadSlideResources(slideDeck->masterSlide(),
+ m_sceneManager->presentation());
+ for (auto object : slideDeck->masterSlide()->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+
+ }
+ }
setInternalState(PlayerState::Ready);
Q_EMIT slideDeckChanged(m_data.slideDeck);
}
@@ -597,6 +611,17 @@ void Q3DSSlidePlayer::setInternalState(Q3DSSlidePlayer::PlayerState state)
{
m_data.pendingState = state;
Q3DSSlide *currentSlide = m_data.slideDeck->currentSlide();
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ for (auto object : currentSlide->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ }
+ }
qCDebug(lcSlidePlayer, "Setting internal state for %s from %s to %s", getSlideId(currentSlide).constData(), getEnumName(m_data.state), getEnumName(m_data.pendingState));
@@ -617,6 +642,17 @@ void Q3DSSlidePlayer::setInternalState(Q3DSSlidePlayer::PlayerState state)
const bool forceUpdate = (!slideChanged && (state == PlayerState::Ready));
if (slideChanged || forceUpdate)
handleCurrentSlideChanged(currentSlide, previousSlide, forceUpdate);
+ if (slideChanged) {
+ currentSlide->setActive(true);
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ if (previousSlide) {
+ previousSlide->setActive(false);
+ if (previousSlide->unloadSlide()) {
+ m_sceneManager->engine()->unloadSlideResources(previousSlide,
+ m_sceneManager->presentation());
+ }
+ }
+ }
if (state == PlayerState::Playing) {
const bool restart = (m_mode == PlayerMode::Viewer)
diff --git a/src/runtime/q3dsuippresentation.cpp b/src/runtime/q3dsuippresentation.cpp
index 0e654c5..56f97a1 100644
--- a/src/runtime/q3dsuippresentation.cpp
+++ b/src/runtime/q3dsuippresentation.cpp
@@ -691,6 +691,20 @@ void Q3DSGraphObject::notifyPropertyChanges(int changeFlags, const QSet<QString>
}
}
+void Q3DSGraphObject::listCustomPropertyResources(
+ QSet<QString> &resources, const QMap<QString, Q3DSMaterial::PropertyElement> &meta) const
+{
+ auto properties = dynamicProperties();
+ for (auto it = properties.cbegin(), itEnd = properties.cend(); it != itEnd; ++it) {
+ const QString &propName(it.key());
+ const Q3DSMaterial::PropertyElement &propMeta(meta[propName]);
+ if (propMeta.type == Q3DS::Texture) {
+ const QVariant &propValue(it.value());
+ resources.insert(propValue.toString());
+ }
+ }
+}
+
// Setters return a change object that can be passed straight to
// notifyPropertyChange (via the changelists' intializer list even). When the
// value does not change, the returned change object has isValid()==false, these
@@ -1646,6 +1660,16 @@ Q3DSPropertyChange Q3DSSlide::setPlayThroughValue(const QVariant &v)
return createPropSetter(m_playThroughValue, v, "playthroughto");
}
+void Q3DSSlide::generateResourceSet(Q3DSUipPresentation *presentation)
+{
+ m_resources.clear();
+ QSet<QString> resources;
+ for (const auto *object : qAsConst(m_objects))
+ object->listResources(resources);
+ for (auto res : qAsConst(resources))
+ m_resources << presentation->imageUrl(res);
+}
+
Q3DSImage::Q3DSImage()
: Q3DSGraphObject(Image)
{
@@ -1721,6 +1745,12 @@ void Q3DSImage::resolveReferences(Q3DSUipPresentation &presentation)
}
}
+void Q3DSImage::listResources(QSet<QString> &resources) const
+{
+ if (!m_sourcePath.isEmpty() && !resources.contains(m_sourcePath))
+ resources.insert(m_sourcePath);
+}
+
namespace {
#if 0
bool scanImageForAlpha(const uchar *data, int width, int height, unsigned pixelSizeInBytes, unsigned alphaSizeInBits, bool isAlphaFirst = false)
@@ -3936,6 +3966,24 @@ Q3DSGraphObject *Q3DSUipPresentation::getObjectByName(const QString &name) const
return nullptr;
}
+QUrl Q3DSUipPresentation::imageUrl(const QString &sourcePath)
+{
+ QUrl ret;
+ if (d->preferKtx) {
+ QString ktxSource = sourcePath;
+ ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.')));
+ ktxSource.append(QLatin1String(".ktx"));
+ QFileInfo info(ktxSource);
+ if (info.exists())
+ ret = QUrl::fromLocalFile(ktxSource);
+ else
+ ret = QUrl::fromLocalFile(sourcePath);
+ } else {
+ ret = QUrl::fromLocalFile(sourcePath);
+ }
+ return ret;
+}
+
namespace {
struct ClonedObject {
Q3DSGraphObject *original;
diff --git a/src/runtime/q3dsuippresentation_p.h b/src/runtime/q3dsuippresentation_p.h
index de1be1e..fde8233 100644
--- a/src/runtime/q3dsuippresentation_p.h
+++ b/src/runtime/q3dsuippresentation_p.h
@@ -406,8 +406,14 @@ public:
State state() const { return m_state; }
+ virtual void listResources(QSet<QString> &resources) const
+ {
+ Q_UNUSED(resources);
+ }
protected:
void destroyGraph();
+ void listCustomPropertyResources(QSet<QString> &resources, const QMap<QString,
+ Q3DSMaterial::PropertyElement> &meta) const;
QByteArray m_id;
QString m_name;
@@ -739,6 +745,23 @@ public:
Q3DSPropertyChange setPlayThrough(PlayThrough v);
Q3DSPropertyChange setPlayThroughValue(const QVariant &v);
+ void generateResourceSet(Q3DSUipPresentation *presentation);
+ QSet<QUrl> resourceSet() const { return m_resources; }
+
+ void setUnloadSlide(bool unload)
+ {
+ m_unload = unload;
+ }
+ void setActive(bool active)
+ {
+ m_active = active;
+ // The slide must be explicitly unloaded by the user every time
+ if (active)
+ m_unload = false;
+ }
+ bool unloadSlide() const { return m_unload; }
+ bool active() const { return m_active; }
+
private:
Q_DISABLE_COPY(Q3DSSlide)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -756,6 +779,9 @@ private:
QVector<Q3DSAction> m_actions;
QVector<SlideGraphChangeCallback> m_slideGraphChangeCallbacks; // master only
QVector<SlideObjectChangeCallback> m_slideObjectChangeCallbacks;
+ QSet<QUrl> m_resources;
+ bool m_unload = false;
+ bool m_active = false;
friend class Q3DSUipParser;
friend class Q3DSGraphObject;
@@ -842,6 +868,8 @@ public:
Q3DSPropertyChange setCustomImage(const QImage &v); // alternative setter
QImage customImage() const { return m_customImage; }
+ void listResources(QSet<QString> &resources) const override;
+
private:
Q_DISABLE_COPY(Q3DSImage)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -1956,6 +1984,10 @@ public:
const Q3DSCustomMaterial *material() const { return &m_material; }
+ void listResources(QSet<QString> &resources) const override
+ {
+ listCustomPropertyResources(resources, m_material.properties());
+ }
private:
Q_DISABLE_COPY(Q3DSCustomMaterialInstance)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -2005,6 +2037,11 @@ public:
const Q3DSEffect *effect() const { return &m_effect; }
+ void listResources(QSet<QString> &resources) const override
+ {
+ listCustomPropertyResources(resources, m_effect.properties());
+ }
+
private:
Q_DISABLE_COPY(Q3DSEffectInstance)
template<typename V> void setProps(const V &attrs, PropSetFlags flags);
@@ -2200,6 +2237,8 @@ public:
void addImplicitPropertyChanges();
QHash<QString, bool> &imageTransparencyHash();
+ QUrl imageUrl(const QString &sourcePath);
+
private:
Q_DISABLE_COPY(Q3DSUipPresentation)
diff --git a/src/runtime/slideplayerng/q3dsslideplayerng.cpp b/src/runtime/slideplayerng/q3dsslideplayerng.cpp
index ac24c30..854c72e 100644
--- a/src/runtime/slideplayerng/q3dsslideplayerng.cpp
+++ b/src/runtime/slideplayerng/q3dsslideplayerng.cpp
@@ -34,6 +34,7 @@
#include <QtCore/qmetaobject.h>
#include "q3dsscenemanager_p.h"
#include "q3dsanimationmanagerng_p.h"
+#include "q3dsengine_p.h"
#include "q3dslogging_p.h"
#include "animator/q3dsanimator_p.h"
@@ -390,6 +391,18 @@ void Q3DSSlidePlayerNg::setSlideDeck(Q3DSSlideDeck *slideDeck)
};
forAllSlides(slideDeck);
+ m_sceneManager->engine()->loadSlideResources(slideDeck->masterSlide(),
+ m_sceneManager->presentation());
+ for (auto object : slideDeck->masterSlide()->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+ }
+ }
setInternalState(PlayerState::Ready);
Q_EMIT slideDeckChanged(m_data.slideDeck);
}
@@ -533,8 +546,28 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState
m_sceneManager->queueEvent(ev);
};
+ Q3DSSlide *oldSlide = m_currentSlide;
Q3DSSlide *currentSlide = m_data.slideDeck->currentSlide();
Q_ASSERT(currentSlide);
+ m_sceneManager->engine()->loadSlideResources(currentSlide, m_sceneManager->presentation());
+ if (m_currentSlide && m_currentSlide != currentSlide) {
+ m_currentSlide->setActive(false);
+ if (m_currentSlide->unloadSlide())
+ m_sceneManager->engine()->unloadSlideResources(m_currentSlide,
+ m_sceneManager->presentation());
+ }
+ m_currentSlide = currentSlide;
+ m_currentSlide->setActive(true);
+ for (auto object : currentSlide->objects()) {
+ if (object->type() == Q3DSGraphObject::Component
+ && object->state() == Q3DSGraphObject::Enabled) {
+ Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(object);
+ m_sceneManager->engine()->loadSlideResources(comp->masterSlide(),
+ m_sceneManager->presentation());
+ m_sceneManager->engine()->loadSlideResources(comp->currentSlide(),
+ m_sceneManager->presentation());
+ }
+ }
Q3DSSlide *activeSlide = (m_type == PlayerType::Scene) ? m_sceneManager->currentSlide()
: m_component->currentSlide();
@@ -612,6 +645,8 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState
m_sceneManager->syncScene();
if (newState != PlayerState::Idle) {
+ if (oldSlide)
+ queueSlideEvent(oldSlide, Q3DSGraphObjectEvents::slideExitEvent());
queueSlideEvent(currentSlide, Q3DSGraphObjectEvents::slideEnterEvent());
Q_EMIT slideChanged(currentSlide);
}
diff --git a/src/runtime/slideplayerng/q3dsslideplayerng_p.h b/src/runtime/slideplayerng/q3dsslideplayerng_p.h
index cdec113..b17991c 100644
--- a/src/runtime/slideplayerng/q3dsslideplayerng_p.h
+++ b/src/runtime/slideplayerng/q3dsslideplayerng_p.h
@@ -118,6 +118,7 @@ private:
float playbackRate = 1.0f;
} m_data;
+ Q3DSSlide *m_currentSlide = nullptr;
Q3DSSceneManager *m_sceneManager;
Q3DSComponentNode *m_component = nullptr;
PlayerMode m_mode = PlayerMode::Viewer;