summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2018-07-11 11:18:24 +0200
committerPaul Lemire <paul.lemire@kdab.com>2018-07-18 13:44:26 +0000
commitcdf92a8ba06c3df17a11c8931d540a4bf9229f61 (patch)
tree4ef115907f603ac321f4bfc97fb6dcecc1637603
parentee53b5366eb925b2fbe3cd28209e2f8c7bc9e143 (diff)
Fix: Do not enforce TextureImage to be parented only by Texture
This behavior prevented using TextureImage not directly parented by the Texture that uses them (assert would be triggered). In turn, this also prevents sharing a TextureImage among several Texture instances which is counter productive since this is where the data is actually stored. This patch fixes this issue. It removes all direct coupling between Texture and TextureImages. Now Texture only contains the list of TextureImage ids it references. This allows to not make look-ups into the TextureImageManager to retrieve handles, which could be an issue if TextureImages have not yet had their backend created. TextureImage doesn't keep track of the referencing texture that uses it anymore. Instead, we let the renderer do the job of checking if any of the TextureImage referenced by a Texture has changed to trigger actual Texture update. Change-Id: I3c63379d0f4b314e9b53f225870eeaded0bb4aec Task-number: QTBUG-69407 Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/render/frontend/qrenderaspect.cpp3
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp37
-rw-r--r--src/render/texture/apitexturemanager_p.h22
-rw-r--r--src/render/texture/qabstracttexture.cpp2
-rw-r--r--src/render/texture/texture.cpp48
-rw-r--r--src/render/texture/texture_p.h15
-rw-r--r--src/render/texture/textureimage.cpp29
-rw-r--r--src/render/texture/textureimage_p.h9
-rw-r--r--tests/auto/render/texture/tst_texture.cpp66
-rw-r--r--tests/auto/render/textures/tst_textures.cpp164
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/shared_texture_image/image.pngbin0 -> 166386 bytes
-rw-r--r--tests/manual/shared_texture_image/main.cpp62
-rw-r--r--tests/manual/shared_texture_image/main.qml144
-rw-r--r--tests/manual/shared_texture_image/shared_texture_image.pro10
-rw-r--r--tests/manual/shared_texture_image/shared_texture_image.qrc6
16 files changed, 508 insertions, 112 deletions
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index 1470448e5..09cb75e46 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -253,9 +253,8 @@ void QRenderAspectPrivate::registerBackendTypes()
q->registerBackendType<Qt3DCore::QJoint>(QSharedPointer<Render::JointFunctor>::create(m_renderer, m_nodeManagers->jointManager(), m_nodeManagers->skeletonManager()));
// Textures
- q->registerBackendType<QAbstractTexture>(QSharedPointer<Render::TextureFunctor>::create(m_renderer, m_nodeManagers->textureManager(), m_nodeManagers->textureImageManager()));
+ q->registerBackendType<QAbstractTexture>(QSharedPointer<Render::TextureFunctor>::create(m_renderer, m_nodeManagers->textureManager()));
q->registerBackendType<QAbstractTextureImage>(QSharedPointer<Render::TextureImageFunctor>::create(m_renderer,
- m_nodeManagers->textureManager(),
m_nodeManagers->textureImageManager(),
m_nodeManagers->textureImageDataManager()));
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index 1e0a43b30..904527cd5 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -1057,13 +1057,42 @@ void Renderer::lookForDownloadableBuffers()
// Executed in a job
void Renderer::lookForDirtyTextures()
{
- const QVector<HTexture> activeTextureHandles = m_nodesManager->textureManager()->activeHandles();
+ // To avoid having Texture or TextureImage maintain relationships between
+ // one another, we instead perform a lookup here to check if a texture
+ // image has been updated to then notify textures referencing the image
+ // that they need to be updated
+ TextureImageManager *imageManager = m_nodesManager->textureImageManager();
+ const QVector<HTextureImage> activeTextureImageHandles = imageManager->activeHandles();
+ Qt3DCore::QNodeIdVector dirtyImageIds;
+ for (const HTextureImage &handle: activeTextureImageHandles) {
+ TextureImage *image = imageManager->data(handle);
+ if (image->isDirty()) {
+ dirtyImageIds.push_back(image->peerId());
+ image->unsetDirty();
+ }
+ }
+
+ TextureManager *textureManager = m_nodesManager->textureManager();
+ const QVector<HTexture> activeTextureHandles = textureManager->activeHandles();
for (const HTexture &handle: activeTextureHandles) {
- Texture *texture = m_nodesManager->textureManager()->data(handle);
+ Texture *texture = textureManager->data(handle);
+ const QNodeIdVector imageIds = texture->textureImageIds();
+
+ // Does the texture reference any of the dirty texture images?
+ for (const QNodeId imageId: imageIds) {
+ if (dirtyImageIds.contains(imageId)) {
+ texture->addDirtyFlag(Texture::DirtyImageGenerators);
+ break;
+ }
+ }
+
// Dirty meaning that something has changed on the texture
// either properties, parameters, generator or a texture image
if (texture->dirtyFlags() != Texture::NotDirty)
m_dirtyTextures.push_back(handle);
+ // Note: texture dirty flags are reset when actually updating the
+ // textures in updateGLResources() as resetting flags here would make
+ // us lose information about what was dirty exactly.
}
}
@@ -1204,7 +1233,7 @@ void Renderer::updateGLResources()
void Renderer::updateTexture(Texture *texture)
{
// Check that the current texture images are still in place, if not, do not update
- const bool isValid = texture->isValid();
+ const bool isValid = texture->isValid(m_nodesManager->textureImageManager());
if (!isValid)
return;
@@ -1278,7 +1307,7 @@ void Renderer::updateTexture(Texture *texture)
// Will make the texture requestUpload
if (dirtyFlags.testFlag(Texture::DirtyImageGenerators) &&
- !glTextureManager->setImages(glTexture, texture->textureImages()))
+ !glTextureManager->setImages(glTexture, texture->textureImageIds()))
qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerators failed, should be non-shared";
// Will make the texture requestUpload
diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h
index 97dc1eb27..58a73e09e 100644
--- a/src/render/texture/apitexturemanager_p.h
+++ b/src/render/texture/apitexturemanager_p.h
@@ -219,7 +219,7 @@ public:
// Change the texture images of the given texture, if it is a non-shared texture
// Return true, if it was changed successfully, false otherwise
- bool setImages(APITexture *tex, const QVector<HTextureImage> &images)
+ bool setImages(APITexture *tex, const Qt3DCore::QNodeIdVector &imageIds)
{
Q_ASSERT(tex);
@@ -227,8 +227,8 @@ public:
return false;
// create Image structs
- QVector<APITextureImage> texImgs = texImgsFromNodes(images);
- if (texImgs.size() != images.size())
+ QVector<APITextureImage> texImgs = texImgsFromNodes(imageIds);
+ if (texImgs.size() != imageIds.size())
return false;
tex->setImages(texImgs);
@@ -293,11 +293,11 @@ private:
// make sure the image generators are the same
const QVector<APITextureImage> texImgGens = tex->images();
- const QVector<HTextureImage> texImgs = texNode->textureImages();
+ const Qt3DCore::QNodeIdVector texImgs = texNode->textureImageIds();
if (texImgGens.size() != texImgs.size())
return false;
for (int i = 0; i < texImgGens.size(); ++i) {
- const TextureImage *img = m_textureImageManager->data(texImgs[i]);
+ const TextureImage *img = m_textureImageManager->lookupResource(texImgs[i]);
Q_ASSERT(img != nullptr);
if (!(*img->dataGenerator() == *texImgGens[i].generator)
|| img->layer() != texImgGens[i].layer
@@ -330,8 +330,8 @@ private:
APITexture *createTexture(const Texture *node, bool unique)
{
// create Image structs
- const QVector<APITextureImage> texImgs = texImgsFromNodes(node->textureImages());
- if (texImgs.empty() && !node->textureImages().empty())
+ const QVector<APITextureImage> texImgs = texImgsFromNodes(node->textureImageIds());
+ if (texImgs.empty() && !node->textureImageIds().empty())
return nullptr;
// no matching shared texture was found, create a new one
@@ -345,13 +345,13 @@ private:
return newTex;
}
- QVector<APITextureImage> texImgsFromNodes(const QVector<HTextureImage> &images) const
+ QVector<APITextureImage> texImgsFromNodes(const Qt3DCore::QNodeIdVector &imageIds) const
{
QVector<APITextureImage> ret;
- ret.resize(images.size());
+ ret.resize(imageIds.size());
- for (int i = 0; i < images.size(); ++i) {
- const TextureImage *img = m_textureImageManager->data(images[i]);
+ for (int i = 0; i < imageIds.size(); ++i) {
+ const TextureImage *img = m_textureImageManager->lookupResource(imageIds[i]);
if (!img) {
qWarning() << "[Qt3DRender::TextureManager] invalid TextureImage handle";
return QVector<APITextureImage>();
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index c4c693852..03746620e 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -630,8 +630,6 @@ void QAbstractTexture::addTextureImage(QAbstractTextureImage *textureImage)
// Ensures proper bookkeeping
d->registerDestructionHelper(textureImage, &QAbstractTexture::removeTextureImage, d->m_textureImages);
- if (textureImage->parent() && textureImage->parent() != this)
- qWarning() << "A QAbstractTextureImage was shared, expect a crash, undefined behavior at best";
// We need to add it as a child of the current node if it has been declared inline
// Or not previously added as a child of the current node so that
// 1) The backend gets notified about it's creation
diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp
index 914a4d9d8..5a50fb30f 100644
--- a/src/render/texture/texture.cpp
+++ b/src/render/texture/texture.cpp
@@ -62,7 +62,6 @@ Texture::Texture()
// We need backend -> frontend notifications to update the status of the texture
: BackendNode(ReadWrite)
, m_dirty(DirtyImageGenerators|DirtyProperties|DirtyParameters|DirtyDataGenerator)
- , m_textureImageManager(nullptr)
{
}
@@ -74,11 +73,6 @@ Texture::~Texture()
// would have been called
}
-void Texture::setTextureImageManager(TextureImageManager *manager)
-{
- m_textureImageManager = manager;
-}
-
void Texture::addDirtyFlag(DirtyFlags flags)
{
QMutexLocker lock(&m_flagsMutex);
@@ -101,34 +95,16 @@ void Texture::unsetDirty()
void Texture::addTextureImage(Qt3DCore::QNodeId id)
{
- if (!m_textureImageManager) {
- qWarning() << "[Qt3DRender::TextureNode] addTextureImage: invalid TextureImageManager";
- return;
- }
-
- const HTextureImage handle = m_textureImageManager->lookupHandle(id);
- if (handle.isNull()) {
- qWarning() << "[Qt3DRender::TextureNode] addTextureImage: image handle is NULL";
- } else if (!m_textureImages.contains(handle)) {
- m_textureImages << handle;
+ if (!m_textureImageIds.contains(id)) {
+ m_textureImageIds.push_back(id);
addDirtyFlag(DirtyImageGenerators);
}
}
void Texture::removeTextureImage(Qt3DCore::QNodeId id)
{
- if (!m_textureImageManager) {
- qWarning() << "[Qt3DRender::TextureNode] removeTextureImage: invalid TextureImageManager";
- return;
- }
-
- const HTextureImage handle = m_textureImageManager->lookupHandle(id);
- if (handle.isNull()) {
- qWarning() << "[Qt3DRender::TextureNode] removeTextureImage: image handle is NULL";
- } else {
- m_textureImages.removeAll(handle);
- addDirtyFlag(DirtyImageGenerators);
- }
+ m_textureImageIds.removeAll(id);
+ addDirtyFlag(DirtyImageGenerators);
}
// This is called by Renderer::updateGLResources
@@ -138,7 +114,7 @@ void Texture::cleanup()
// Whoever calls this must make sure to also check if this
// texture is being referenced by a shared API specific texture (GLTexture)
m_dataFunctor.reset();
- m_textureImages.clear();
+ m_textureImageIds.clear();
// set default values
m_properties.width = 1;
@@ -321,10 +297,10 @@ void Texture::updateFromData(QTextureDataPtr data)
}
}
-bool Texture::isValid() const
+bool Texture::isValid(TextureImageManager *manager) const
{
- for (const auto &handle : m_textureImages) {
- TextureImage *img = m_textureImageManager->data(handle);
+ for (const QNodeId id : m_textureImageIds) {
+ TextureImage *img = manager->lookupResource(id);
if (img == nullptr)
return false;
}
@@ -354,23 +330,23 @@ void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chan
m_parameters.comparisonMode = data.comparisonMode;
m_dataFunctor = data.dataFunctor;
+ for (const QNodeId imgId : data.textureImageIds)
+ addTextureImage(imgId);
+
addDirtyFlag(DirtyFlags(DirtyImageGenerators|DirtyProperties|DirtyParameters));
}
TextureFunctor::TextureFunctor(AbstractRenderer *renderer,
- TextureManager *textureNodeManager,
- TextureImageManager *textureImageManager)
+ TextureManager *textureNodeManager)
: m_renderer(renderer)
, m_textureNodeManager(textureNodeManager)
- , m_textureImageManager(textureImageManager)
{
}
Qt3DCore::QBackendNode *TextureFunctor::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
{
Texture *backend = m_textureNodeManager->getOrCreateResource(change->subjectId());
- backend->setTextureImageManager(m_textureImageManager);
backend->setRenderer(m_renderer);
// Remove id from cleanupList if for some reason we were in the dirty list of texture
// (Can happen when a node destroyed is followed by a node created change
diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h
index 7e23f124c..789285644 100644
--- a/src/render/texture/texture_p.h
+++ b/src/render/texture/texture_p.h
@@ -140,8 +140,6 @@ public:
};
Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
- void setTextureImageManager(TextureImageManager *manager);
-
void addDirtyFlag(DirtyFlags flags);
DirtyFlags dirtyFlags();
void unsetDirty();
@@ -154,13 +152,13 @@ public:
inline const TextureProperties& properties() const { return m_properties; }
inline const TextureParameters& parameters() const { return m_parameters; }
- inline const QVector<HTextureImage>& textureImages() const { return m_textureImages; }
+ inline const Qt3DCore::QNodeIdVector textureImageIds() const { return m_textureImageIds; }
inline const QTextureGeneratorPtr& dataGenerator() const { return m_dataFunctor; }
void notifyStatus(QAbstractTexture::Status status);
void updateFromData(QTextureDataPtr data);
- bool isValid() const;
+ bool isValid(TextureImageManager *manager) const;
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
@@ -169,9 +167,8 @@ private:
TextureParameters m_parameters;
QTextureGeneratorPtr m_dataFunctor;
- QVector<HTextureImage> m_textureImages;
+ Qt3DCore::QNodeIdVector m_textureImageIds;
- TextureImageManager *m_textureImageManager;
QMutex m_flagsMutex;
};
@@ -179,8 +176,7 @@ class Q_AUTOTEST_EXPORT TextureFunctor : public Qt3DCore::QBackendNodeMapper
{
public:
explicit TextureFunctor(AbstractRenderer *renderer,
- TextureManager *textureNodeManager,
- TextureImageManager *textureImageManager);
+ TextureManager *textureNodeManager);
Qt3DCore::QBackendNode *create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const final;
Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const final;
void destroy(Qt3DCore::QNodeId id) const final;
@@ -188,14 +184,13 @@ public:
private:
AbstractRenderer *m_renderer;
TextureManager *m_textureNodeManager;
- TextureImageManager *m_textureImageManager;
};
#ifndef QT_NO_DEBUG_STREAM
inline QDebug operator<<(QDebug dbg, const Texture &texture)
{
QDebugStateSaver saver(dbg);
- dbg << "QNodeId =" << texture.peerId() << "imageCount =" << texture.textureImages().size() << endl;
+ dbg << "QNodeId =" << texture.peerId() << "imageCount =" << texture.textureImageIds().size() << endl;
return dbg;
}
#endif
diff --git a/src/render/texture/textureimage.cpp b/src/render/texture/textureimage.cpp
index b718f237b..880562b87 100644
--- a/src/render/texture/textureimage.cpp
+++ b/src/render/texture/textureimage.cpp
@@ -53,10 +53,10 @@ namespace Render {
TextureImage::TextureImage()
: BackendNode(ReadWrite)
+ , m_dirty(false)
, m_layer(0)
, m_mipLevel(0)
, m_face(QAbstractTexture::CubeMapPositiveX)
- , m_textureManager(nullptr)
, m_textureImageDataManager(nullptr)
{
}
@@ -71,6 +71,7 @@ void TextureImage::cleanup()
m_textureImageDataManager->releaseData(m_generator, peerId());
m_generator.reset();
}
+ m_dirty = false;
m_layer = 0;
m_mipLevel = 0;
m_face = QAbstractTexture::CubeMapPositiveX;
@@ -84,18 +85,8 @@ void TextureImage::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr
m_layer = data.layer;
m_face = data.face;
m_generator = data.generator;
+ m_dirty = true;
- if (!change->parentId()) {
- qWarning() << "No QAbstractTexture parent found";
- } else {
- const QNodeId id = change->parentId();
- m_textureProvider = m_textureManager->lookupHandle(id);
- Texture *texture = m_textureManager->data(m_textureProvider);
- Q_ASSERT(texture);
- // Notify the Texture that it has a new TextureImage and needs an update
- texture->addTextureImage(peerId());
-
- }
// Request functor upload
if (m_generator)
m_textureImageDataManager->requestData(m_generator, peerId());
@@ -121,25 +112,22 @@ void TextureImage::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
if (m_generator)
m_textureImageDataManager->requestData(m_generator, peerId());
}
-
- // Notify the Texture that we were updated and request it to schedule an update job
- Texture *txt = m_textureManager->data(m_textureProvider);
- if (txt != nullptr)
- txt->addDirtyFlag(Texture::DirtyImageGenerators);
-
+ m_dirty = true;
}
markDirty(AbstractRenderer::AllDirty);
BackendNode::sceneChangeEvent(e);
}
+void TextureImage::unsetDirty()
+{
+ m_dirty = false;
+}
TextureImageFunctor::TextureImageFunctor(AbstractRenderer *renderer,
- TextureManager *textureManager,
TextureImageManager *textureImageManager,
TextureImageDataManager *textureImageDataManager)
: m_renderer(renderer)
- , m_textureManager(textureManager)
, m_textureImageManager(textureImageManager)
, m_textureImageDataManager(textureImageDataManager)
{
@@ -148,7 +136,6 @@ TextureImageFunctor::TextureImageFunctor(AbstractRenderer *renderer,
Qt3DCore::QBackendNode *TextureImageFunctor::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
{
TextureImage *backend = m_textureImageManager->getOrCreateResource(change->subjectId());
- backend->setTextureManager(m_textureManager);
backend->setTextureImageDataManager(m_textureImageDataManager);
backend->setRenderer(m_renderer);
return backend;
diff --git a/src/render/texture/textureimage_p.h b/src/render/texture/textureimage_p.h
index b7a1fae8e..19801ee77 100644
--- a/src/render/texture/textureimage_p.h
+++ b/src/render/texture/textureimage_p.h
@@ -78,10 +78,8 @@ public:
void cleanup();
- void setTextureManager(TextureManager *manager) { m_textureManager = manager; }
void setTextureImageDataManager(TextureImageDataManager *dataManager) { m_textureImageDataManager = dataManager; }
- TextureManager *textureManager() const { return m_textureManager; }
TextureImageDataManager *textureImageDataManager() const { return m_textureImageDataManager; }
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
@@ -90,25 +88,25 @@ public:
inline int mipLevel() const { return m_mipLevel; }
inline QAbstractTexture::CubeMapFace face() const { return m_face; }
inline QTextureImageDataGeneratorPtr dataGenerator() const { return m_generator; }
+ inline bool isDirty() const { return m_dirty; }
+ void unsetDirty();
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
+ bool m_dirty;
int m_layer;
int m_mipLevel;
QAbstractTexture::CubeMapFace m_face;
QTextureImageDataGeneratorPtr m_generator;
- TextureManager *m_textureManager;
TextureImageDataManager *m_textureImageDataManager;
- HTexture m_textureProvider;
};
class TextureImageFunctor : public Qt3DCore::QBackendNodeMapper
{
public:
explicit TextureImageFunctor(AbstractRenderer *renderer,
- TextureManager *textureManager,
TextureImageManager *textureImageManager,
TextureImageDataManager *textureImageDataManager);
@@ -118,7 +116,6 @@ public:
private:
AbstractRenderer *m_renderer;
- TextureManager *m_textureManager;
TextureImageManager *m_textureImageManager;
TextureImageDataManager *m_textureImageDataManager;
};
diff --git a/tests/auto/render/texture/tst_texture.cpp b/tests/auto/render/texture/tst_texture.cpp
index 784186690..2e94cda24 100644
--- a/tests/auto/render/texture/tst_texture.cpp
+++ b/tests/auto/render/texture/tst_texture.cpp
@@ -29,6 +29,8 @@
#include <QtTest/QTest>
#include <qbackendnodetester.h>
#include <Qt3DCore/qdynamicpropertyupdatedchange.h>
+#include <Qt3DCore/qpropertynodeaddedchange.h>
+#include <Qt3DCore/qpropertynoderemovedchange.h>
#include <Qt3DRender/private/texture_p.h>
#include "testpostmanarbiter.h"
@@ -57,6 +59,7 @@ private slots:
void checkFrontendPropertyNotifications();
void checkPropertyMirroring();
void checkPropertyChanges();
+ void checkTextureImageBookeeping();
};
void tst_RenderTexture::checkFrontendPropertyNotifications()
@@ -165,6 +168,31 @@ void tst_RenderTexture::checkFrontendPropertyNotifications()
// THEN
QCOMPARE(arbiter.events.size(), 0);
+
+ // WHEN
+ Qt3DRender::QTextureImage img;
+ texture.addTextureImage(&img);
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ const auto addedChange = arbiter.events.first().staticCast<Qt3DCore::QPropertyNodeAddedChange>();
+ QCOMPARE(addedChange->propertyName(), "textureImage");
+ QCOMPARE(addedChange->addedNodeId(), img.id());
+ QCOMPARE(addedChange->type(), Qt3DCore::PropertyValueAdded);
+
+ arbiter.events.clear();
+
+ // WHEN
+ texture.removeTextureImage(&img);
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ const auto removedChange = arbiter.events.first().staticCast<Qt3DCore::QPropertyNodeRemovedChange>();
+ QCOMPARE(removedChange->propertyName(), "textureImage");
+ QCOMPARE(removedChange->removedNodeId(), img.id());
+ QCOMPARE(removedChange->type(), Qt3DCore::PropertyValueRemoved);
+
+ arbiter.events.clear();
}
template <typename FrontendTextureType, Qt3DRender::QAbstractTexture::Target Target>
@@ -269,6 +297,44 @@ void tst_RenderTexture::checkPropertyChanges()
QCOMPARE(backend.properties().samples, 64);
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TexturesDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+
+ // WHEN
+ Qt3DRender::QTextureImage img;
+ const auto imageAddChange = Qt3DCore::QPropertyNodeAddedChangePtr::create(Qt3DCore::QNodeId(), &img);
+ imageAddChange->setPropertyName("textureImage");
+ backend.sceneChangeEvent(imageAddChange);
+
+ // THEN
+ QCOMPARE(backend.textureImageIds().size(), 1);
+ QCOMPARE(backend.textureImageIds().first(), img.id());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TexturesDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+}
+
+void tst_RenderTexture::checkTextureImageBookeeping()
+{
+ // GIVEN
+ TestArbiter arbiter;
+ DummyTexture texture;
+ arbiter.setArbiterOnNode(&texture);
+
+ QCoreApplication::processEvents();
+
+ {
+ // WHEN
+ Qt3DRender::QTextureImage img;
+ texture.addTextureImage(&img);
+
+ // THEN
+ QCOMPARE(img.parent(), &texture);
+ QCOMPARE(texture.textureImages().size(), 1);
+ QCOMPARE(texture.textureImages().first()->id(), img.id());
+ }
+ // WHEN -> img is destroyed
+
+ // THEN
+ QCOMPARE(texture.textureImages().size(), 0);
+
}
QTEST_APPLESS_MAIN(tst_RenderTexture)
diff --git a/tests/auto/render/textures/tst_textures.cpp b/tests/auto/render/textures/tst_textures.cpp
index 364bfd7a9..f1b72b33b 100644
--- a/tests/auto/render/textures/tst_textures.cpp
+++ b/tests/auto/render/textures/tst_textures.cpp
@@ -132,6 +132,20 @@ protected:
int m_genId;
};
+class EmptyTextureImage : public Qt3DRender::QAbstractTextureImage
+{
+public:
+ EmptyTextureImage(Qt3DCore::QNode *p = nullptr)
+ : QAbstractTextureImage(p)
+ {
+ }
+
+ Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const
+ {
+ return {};
+ }
+};
+
class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
{
Q_OBJECT
@@ -155,7 +169,6 @@ class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
Qt3DRender::Render::TextureImageDataManager *texImgDataManager)
{
Qt3DRender::Render::Texture *backend = texMgr->getOrCreateResource(frontend->id());
- backend->setTextureImageManager(texImgMgr);
simulateInitialization(frontend, backend);
// create texture images
@@ -163,7 +176,6 @@ class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
// make sure TextureImageManager has backend node for this QTextureImage
if (!texImgMgr->contains(texImgFrontend->id())) {
Qt3DRender::Render::TextureImage *texImgBackend = texImgMgr->getOrCreateResource(texImgFrontend->id());
- texImgBackend->setTextureManager(texMgr);
texImgBackend->setTextureImageDataManager(texImgDataManager);
simulateInitialization(texImgFrontend, texImgBackend);
}
@@ -332,9 +344,9 @@ private Q_SLOTS:
// THEN
QCOMPARE(img.layer(), 0);
QCOMPARE(img.mipLevel(), 0);
+ QCOMPARE(img.isDirty(), false);
QCOMPARE(img.face(), Qt3DRender::QAbstractTexture::CubeMapPositiveX);
QVERIFY(img.dataGenerator().isNull());
- QVERIFY(img.textureManager() == nullptr);
QVERIFY(img.textureImageDataManager() == nullptr);
}
@@ -351,17 +363,16 @@ private Q_SLOTS:
// WHEN
Qt3DRender::Render::TextureImage texImgBackend;
- texImgBackend.setTextureManager(texMgr);
texImgBackend.setTextureImageDataManager(texImgDataMgr);
simulateInitialization(&img, &texImgBackend);
texImgBackend.cleanup();
// THEN
+ QCOMPARE(texImgBackend.isDirty(), false);
QCOMPARE(texImgBackend.layer(), 0);
QCOMPARE(texImgBackend.mipLevel(), 0);
QCOMPARE(texImgBackend.face(), Qt3DRender::QAbstractTexture::CubeMapPositiveX);
QVERIFY(texImgBackend.dataGenerator().isNull());
- QVERIFY(texImgBackend.textureManager() != nullptr);
QVERIFY(texImgBackend.textureImageDataManager() != nullptr);
}
@@ -369,7 +380,6 @@ private Q_SLOTS:
{
// GIVEN
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
- Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
Qt3DRender::Render::TextureImageDataManager *texImgDataMgr = mgrs->textureImageDataManager();
TestTextureImage img(1);
@@ -380,12 +390,12 @@ private Q_SLOTS:
img.setMipLevel(3);
Qt3DRender::Render::TextureImage texImgBackend;
- texImgBackend.setTextureManager(texMgr);
texImgBackend.setTextureImageDataManager(texImgDataMgr);
simulateInitialization(&img, &texImgBackend);
// THEN
QCOMPARE(texImgBackend.isEnabled(), true);
+ QCOMPARE(texImgBackend.isDirty(), true);
QCOMPARE(texImgBackend.peerId(), img.id());
QCOMPARE(texImgBackend.layer(), 2);
QCOMPARE(texImgBackend.mipLevel(), 3);
@@ -398,7 +408,6 @@ private Q_SLOTS:
img.setEnabled(false);
Qt3DRender::Render::TextureImage texImgBackend;
- texImgBackend.setTextureManager(texMgr);
texImgBackend.setTextureImageDataManager(texImgDataMgr);
simulateInitialization(&img, &texImgBackend);
@@ -412,12 +421,10 @@ private Q_SLOTS:
{
// GIVEN
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
- Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
Qt3DRender::Render::TextureImageDataManager *texImgDataMgr = mgrs->textureImageDataManager();
Qt3DRender::Render::TextureImage backendImage;
TestRenderer renderer;
backendImage.setRenderer(&renderer);
- backendImage.setTextureManager(texMgr);
backendImage.setTextureImageDataManager(texImgDataMgr);
{
@@ -430,8 +437,10 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendImage.isEnabled(), newValue);
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
}
{
@@ -444,8 +453,10 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendImage.layer(), newValue);
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
}
{
@@ -458,8 +469,10 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendImage.mipLevel(), newValue);
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
}
{
@@ -472,8 +485,10 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendImage.face(), newValue);
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
}
{
@@ -487,8 +502,10 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendImage.dataGenerator(), generator1);
QVERIFY(texImgDataMgr->contains(generator1));
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
// WHEN
Qt3DRender::QTextureImageDataGeneratorPtr generator2(new TestImageDataGenerator(1584));
@@ -500,8 +517,10 @@ private Q_SLOTS:
// THEN
QVERIFY(!texImgDataMgr->contains(generator1));
QVERIFY(texImgDataMgr->contains(generator2));
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
// WHEN
Qt3DRender::QTextureImageDataGeneratorPtr generator3;
@@ -514,8 +533,10 @@ private Q_SLOTS:
QVERIFY(!texImgDataMgr->contains(generator1));
QVERIFY(!texImgDataMgr->contains(generator2));
QVERIFY(backendImage.dataGenerator().isNull());
+ QVERIFY(backendImage.isDirty());
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
}
}
@@ -532,11 +553,10 @@ private Q_SLOTS:
Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
Qt3DRender::Render::Texture *backendTexture = texMgr->getOrCreateResource(frontendTexture->id());
- backendTexture->setTextureImageManager(texImgMgr);
simulateInitialization(frontendTexture, backendTexture);
// THEN
- QCOMPARE(backendTexture->textureImages().size(), 0);
+ QCOMPARE(backendTexture->textureImageIds().size(), 1);
QCOMPARE(frontendTexture->textureImages().size(), 1);
// WHEN
@@ -551,7 +571,6 @@ private Q_SLOTS:
// WHEN
Qt3DRender::Render::TextureImage *texImgBackend = texImgMgr->getOrCreateResource(texImgFrontend->id());
- texImgBackend->setTextureManager(texMgr);
texImgBackend->setTextureImageDataManager(texImgDataMgr);
simulateInitialization(texImgFrontend, texImgBackend);
@@ -589,12 +608,10 @@ private Q_SLOTS:
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
- Qt3DRender::Render::TextureImageManager *texImgMgr = mgrs->textureImageManager();
renderer.setNodeManagers(mgrs.data());
Qt3DRender::Render::TextureFunctor textureBackendNodeMapper(&renderer,
- texMgr,
- texImgMgr);
+ texMgr);
// GIVEN
Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
@@ -622,12 +639,10 @@ private Q_SLOTS:
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
- Qt3DRender::Render::TextureImageManager *texImgMgr = mgrs->textureImageManager();
renderer.setNodeManagers(mgrs.data());
Qt3DRender::Render::TextureFunctor textureBackendNodeMapper(&renderer,
- texMgr,
- texImgMgr);
+ texMgr);
// GIVEN
Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
@@ -652,6 +667,117 @@ private Q_SLOTS:
QCOMPARE(texMgr->textureIdsToCleanup().size(), 0);
QCOMPARE(texMgr->lookupResource(frontendTexture->id()), backendTexture);
}
+
+ void checkTextureImageDirtinessPropagatesToTextures()
+ {
+ // GIVEN
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
+ Qt3DRender::Render::TextureImageManager *texImgMgr = mgrs->textureImageManager();
+ renderer.setNodeManagers(mgrs.data());
+
+ Qt3DRender::QTexture2D *texture1 = new Qt3DRender::QTexture2D();
+ Qt3DRender::QAbstractTextureImage *image1 = new EmptyTextureImage();
+
+ Qt3DRender::QTexture2D *texture2 = new Qt3DRender::QTexture2D();
+ Qt3DRender::QAbstractTextureImage *image2 = new EmptyTextureImage();
+
+ Qt3DRender::QTexture2D *texture3 = new Qt3DRender::QTexture2D();
+
+ texture1->addTextureImage(image1);
+ texture2->addTextureImage(image2);
+ texture3->addTextureImage(image1);
+ texture3->addTextureImage(image2);
+
+ Qt3DRender::Render::Texture *backendTexture1 = texMgr->getOrCreateResource(texture1->id());
+ Qt3DRender::Render::Texture *backendTexture2 = texMgr->getOrCreateResource(texture2->id());
+ Qt3DRender::Render::Texture *backendTexture3 = texMgr->getOrCreateResource(texture3->id());
+ Qt3DRender::Render::TextureImage *backendImage1 = texImgMgr->getOrCreateResource(image1->id());
+ Qt3DRender::Render::TextureImage *backendImage2 = texImgMgr->getOrCreateResource(image2->id());
+
+ simulateInitialization(texture1, backendTexture1);
+ simulateInitialization(texture2, backendTexture2);
+ simulateInitialization(texture3, backendTexture3);
+ simulateInitialization(image1, backendImage1);
+ simulateInitialization(image2, backendImage2);
+
+ backendTexture1->setRenderer(&renderer);
+ backendTexture2->setRenderer(&renderer);
+ backendTexture3->setRenderer(&renderer);
+ backendImage1->setRenderer(&renderer);
+ backendImage2->setRenderer(&renderer);
+
+
+ // THEN
+ QCOMPARE(backendTexture1->textureImageIds().size(), 1);
+ QCOMPARE(backendTexture1->textureImageIds().first(), image1->id());
+ QCOMPARE(backendTexture2->textureImageIds().size(), 1);
+ QCOMPARE(backendTexture2->textureImageIds().first(), image2->id());
+ QCOMPARE(backendTexture3->textureImageIds().size(), 2);
+ QCOMPARE(backendTexture3->textureImageIds().first(), image1->id());
+ QCOMPARE(backendTexture3->textureImageIds().last(), image2->id());
+
+ // WHEN
+ backendTexture1->unsetDirty();
+ backendTexture2->unsetDirty();
+ backendTexture3->unsetDirty();
+ backendImage1->unsetDirty();
+ backendImage2->unsetDirty();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ // Make Image1 dirty
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("dataGenerator");
+ backendImage1->sceneChangeEvent(change);
+
+ // THEN
+ QVERIFY(backendImage1->isDirty());
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+
+ backendImage1->unsetDirty();
+ backendTexture1->unsetDirty();
+ backendTexture3->unsetDirty();
+
+ // WHEN
+ backendImage2->sceneChangeEvent(change);
+
+ // THEN
+ QVERIFY(backendImage2->isDirty());
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+ QVERIFY(backendTexture3->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+ }
};
QTEST_MAIN(tst_RenderTextures)
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index b1e16fc0b..3dcdae8a0 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -56,7 +56,8 @@ SUBDIRS += \
proximityfilter \
rendercapture-qml-fbo \
blitframebuffer-qml \
- raycasting-qml
+ raycasting-qml \
+ shared_texture_image
qtHaveModule(widgets): {
SUBDIRS += \
diff --git a/tests/manual/shared_texture_image/image.png b/tests/manual/shared_texture_image/image.png
new file mode 100644
index 000000000..6ab28d44a
--- /dev/null
+++ b/tests/manual/shared_texture_image/image.png
Binary files differ
diff --git a/tests/manual/shared_texture_image/main.cpp b/tests/manual/shared_texture_image/main.cpp
new file mode 100644
index 000000000..01e3080d2
--- /dev/null
+++ b/tests/manual/shared_texture_image/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Qt3DQuickExtras/qt3dquickwindow.h>
+#include <QGuiApplication>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+ Qt3DExtras::Quick::Qt3DQuickWindow view;
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/shared_texture_image/main.qml b/tests/manual/shared_texture_image/main.qml
new file mode 100644
index 000000000..577649f63
--- /dev/null
+++ b/tests/manual/shared_texture_image/main.qml
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2 as QQ2
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { }
+ ]
+
+ // 2 Textures can reference the same texture image
+ // (Though it's up to the user to make sure that the images are compatible with the textures)
+ // If the 2 Textures contain the exact same number of images and filtering options
+ // Qt3D will only generate 1 actual GL texture, if they differ, 2 will be generated
+ TextureImage {
+ id: mainImage
+ source: "qrc:/image.png"
+ }
+
+ readonly property GeometryRenderer mesh: CuboidMesh {}
+
+ Entity {
+ id: leftCube
+
+ readonly property Transform transform: Transform {
+ translation: Qt.vector3d(-10, 0, 0)
+ scale: 10
+ }
+
+ readonly property DiffuseMapMaterial material: DiffuseMapMaterial {
+ diffuse: Texture2D {
+ generateMipMaps: false
+ minificationFilter: Texture.Nearest
+ magnificationFilter: Texture.Nearest
+ wrapMode {
+ x: WrapMode.ClampToEdge
+ y: WrapMode.ClampToEdge
+ }
+ textureImages: [mainImage]
+ }
+ }
+ components: [ mesh, material, transform ]
+ }
+
+ Entity {
+ id: rightCube
+
+ readonly property Transform transform: Transform {
+ translation: Qt.vector3d(10, 0, 0)
+ scale: 10
+ }
+
+ readonly property DiffuseMapMaterial material: DiffuseMapMaterial {
+ diffuse: Texture2D {
+ generateMipMaps: true
+ minificationFilter: Texture.LinearMipMapLinear
+ magnificationFilter: Texture.Linear
+ maximumAnisotropy: 16.0
+ wrapMode {
+ x: WrapMode.ClampToEdge
+ y: WrapMode.ClampToEdge
+ }
+ textureImages: [mainImage]
+ }
+ }
+ components: [ mesh, material, transform ]
+ }
+}
diff --git a/tests/manual/shared_texture_image/shared_texture_image.pro b/tests/manual/shared_texture_image/shared_texture_image.pro
new file mode 100644
index 000000000..833935698
--- /dev/null
+++ b/tests/manual/shared_texture_image/shared_texture_image.pro
@@ -0,0 +1,10 @@
+QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES += \
+ main.qml
+
+RESOURCES += \
+ shared_texture_image.qrc
diff --git a/tests/manual/shared_texture_image/shared_texture_image.qrc b/tests/manual/shared_texture_image/shared_texture_image.qrc
new file mode 100644
index 000000000..879435bec
--- /dev/null
+++ b/tests/manual/shared_texture_image/shared_texture_image.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>image.png</file>
+ </qresource>
+</RCC>