diff options
Diffstat (limited to 'src/render/texture')
-rw-r--r-- | src/render/texture/apitexturemanager_p.h | 17 | ||||
-rw-r--r-- | src/render/texture/gltexture.cpp | 59 | ||||
-rw-r--r-- | src/render/texture/gltexture_p.h | 15 | ||||
-rw-r--r-- | src/render/texture/qabstracttexture.cpp | 5 | ||||
-rw-r--r-- | src/render/texture/qabstracttexture_p.h | 4 | ||||
-rw-r--r-- | src/render/texture/qpaintedtextureimage.cpp | 44 | ||||
-rw-r--r-- | src/render/texture/qtexture.cpp | 202 | ||||
-rw-r--r-- | src/render/texture/qtexture_p.h | 28 | ||||
-rw-r--r-- | src/render/texture/texture.cpp | 28 | ||||
-rw-r--r-- | src/render/texture/texture_p.h | 7 | ||||
-rw-r--r-- | src/render/texture/textureimage.cpp | 2 |
11 files changed, 307 insertions, 104 deletions
diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h index 2fd340e7e..91747b3bc 100644 --- a/src/render/texture/apitexturemanager_p.h +++ b/src/render/texture/apitexturemanager_p.h @@ -237,6 +237,21 @@ public: return true; } + // Change the texture data generator for given texture, if it is a non-shared texture + // Return true, if it was changed successfully, false otherwise + bool setGenerator(APITexture *tex, const QTextureGeneratorPtr &generator) + { + Q_ASSERT(tex); + + if (isShared(tex)) + return false; + + tex->setGenerator(generator); + m_updatedTextures.push_back(tex); + + return true; + } + // Retrieves abandoned textures. This should be regularly called from the OpenGL thread // to make sure needed GL resources are de-allocated. QVector<APITexture*> takeAbandonedTextures() @@ -258,7 +273,7 @@ public: if (impl->isUnique()) return false; - auto it = m_sharedTextures.find(impl); + auto it = m_sharedTextures.constFind(impl); if (it == m_sharedTextures.cend()) return false; diff --git a/src/render/texture/gltexture.cpp b/src/render/texture/gltexture.cpp index 0fc37891d..854789e94 100644 --- a/src/render/texture/gltexture.cpp +++ b/src/render/texture/gltexture.cpp @@ -120,7 +120,7 @@ QOpenGLTexture* GLTexture::getOrCreateGLTexture() if (m_properties.target != QAbstractTexture::TargetAutomatic) qWarning() << "[Qt3DRender::GLTexture] When a texture provides a generator, it's target is expected to be TargetAutomatic"; - m_properties.target = m_textureData->target(); + m_actualTarget = m_textureData->target(); m_properties.width = m_textureData->width(); m_properties.height = m_textureData->height(); m_properties.depth = m_textureData->depth(); @@ -188,6 +188,8 @@ QOpenGLTexture* GLTexture::getOrCreateGLTexture() if (!m_gl) { m_gl = buildGLTexture(); + if (!m_gl) + return nullptr; m_gl->allocateStorage(); if (!m_gl->isStorageAllocated()) { qWarning() << Q_FUNC_INFO << "texture storage allocation failed"; @@ -224,6 +226,7 @@ void GLTexture::setProperties(const TextureProperties &props) if (m_properties != props) { m_properties = props; QMutexLocker locker(&m_dirtyFlagMutex); + m_actualTarget = props.target; m_dirty |= Properties; } } @@ -278,6 +281,26 @@ void GLTexture::setImages(const QVector<Image> &images) } } +void GLTexture::setGenerator(const QTextureGeneratorPtr &generator) +{ + // Note: we do not compare if the generator is different + // as in some cases we may want to reset the same generator to force a reload + // e.g when using remote urls for textures + if (m_dataFunctor) + m_textureDataManager->releaseData(m_dataFunctor, this); + + m_textureData.reset(); + m_dataFunctor = generator; + + if (m_dataFunctor) { + m_textureDataManager->requestData(m_dataFunctor, this); + requestUpload(); + } +} + +// Return nullptr if +// - context cannot be obtained +// - texture hasn't yet been loaded QOpenGLTexture *GLTexture::buildGLTexture() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -286,12 +309,14 @@ QOpenGLTexture *GLTexture::buildGLTexture() return nullptr; } - if (m_properties.target == QAbstractTexture::TargetAutomatic) { - qWarning() << Q_FUNC_INFO << "something went wrong, target shouldn't be automatic at this point"; + if (m_actualTarget == QAbstractTexture::TargetAutomatic) { + // If the target is automatic at this point, it means that the texture + // hasn't been loaded yet (case of remote urls) and that loading failed + // and that target format couldn't be deduced return nullptr; } - QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_properties.target)); + QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_actualTarget)); // m_format may not be ES2 compatible. Now it's time to convert it, if necessary. QAbstractTexture::TextureFormat format = m_properties.format; @@ -329,16 +354,16 @@ QOpenGLTexture *GLTexture::buildGLTexture() static_cast<QOpenGLTexture::TextureFormat>(format)); glTex->setSize(m_properties.width, m_properties.height, m_properties.depth); // Set layers count if texture array - if (m_properties.target == QAbstractTexture::Target1DArray || - m_properties.target == QAbstractTexture::Target2DArray || - m_properties.target == QAbstractTexture::Target3D || - m_properties.target == QAbstractTexture::Target2DMultisampleArray || - m_properties.target == QAbstractTexture::TargetCubeMapArray) { + if (m_actualTarget == QAbstractTexture::Target1DArray || + m_actualTarget == QAbstractTexture::Target2DArray || + m_actualTarget == QAbstractTexture::Target3D || + m_actualTarget == QAbstractTexture::Target2DMultisampleArray || + m_actualTarget == QAbstractTexture::TargetCubeMapArray) { glTex->setLayers(m_properties.layers); } - if (m_properties.target == QAbstractTexture::Target2DMultisample || - m_properties.target == QAbstractTexture::Target2DMultisampleArray) { + if (m_actualTarget == QAbstractTexture::Target2DMultisample || + m_actualTarget == QAbstractTexture::Target2DMultisampleArray) { // Set samples count if multisampled texture // (multisampled textures don't have mipmaps) glTex->setSamples(m_properties.samples); @@ -384,7 +409,7 @@ void GLTexture::uploadGLTextureData() for (int layer = 0; layer < data->layers(); layer++) { for (int face = 0; face < data->faces(); face++) { for (int level = 0; level < mipLevels; level++) { - // ensure we don't accidently cause a detach / copy of the raw bytes + // ensure we don't accidentally cause a detach / copy of the raw bytes const QByteArray bytes(data->data(layer, face, level)); uploadGLData(m_gl, level, layer, static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face), @@ -399,7 +424,7 @@ void GLTexture::uploadGLTextureData() for (int i = 0; i < m_images.size(); i++) { const QTextureImageDataPtr &imgData = m_imageData.at(i); - // ensure we don't accidently cause a detach / copy of the raw bytes + // ensure we don't accidentally cause a detach / copy of the raw bytes const QByteArray bytes(imgData->data()); uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer, static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face), @@ -410,11 +435,11 @@ void GLTexture::uploadGLTextureData() void GLTexture::updateGLTextureParameters() { m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX)); - if (m_properties.target != QAbstractTexture::Target1D && - m_properties.target != QAbstractTexture::Target1DArray && - m_properties.target != QAbstractTexture::TargetBuffer) + if (m_actualTarget != QAbstractTexture::Target1D && + m_actualTarget != QAbstractTexture::Target1DArray && + m_actualTarget != QAbstractTexture::TargetBuffer) m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY)); - if (m_properties.target == QAbstractTexture::Target3D) + if (m_actualTarget == QAbstractTexture::Target3D) m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ)); m_gl->setMinMagFilters(static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter), static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter)); diff --git a/src/render/texture/gltexture_p.h b/src/render/texture/gltexture_p.h index 424e77854..4c0957f76 100644 --- a/src/render/texture/gltexture_p.h +++ b/src/render/texture/gltexture_p.h @@ -148,6 +148,16 @@ public: m_dirty |= TextureData; } + bool isDirty() + { + QMutexLocker locker(&m_dirtyFlagMutex); + return m_dirty == 0 ? false : true; + } + + QMutex *textureLock() + { + return &m_dirtyFlagMutex; + } protected: template<class APITexture, class APITextureImage> @@ -164,11 +174,12 @@ protected: void setParameters(const TextureParameters ¶ms); void setProperties(const TextureProperties &props); void setImages(const QVector<Image> &images); + void setGenerator(const QTextureGeneratorPtr &generator); private: enum DirtyFlag { - TextureData = 0x01, // one or more generators have been executed, data needs uploading to GPU + TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU Properties = 0x02, // texture needs to be (re-)created Parameters = 0x04 // texture parameters need to be (re-)set @@ -188,6 +199,8 @@ private: TextureDataManager *m_textureDataManager; TextureImageDataManager *m_textureImageDataManager; + // target which is actually used for GL texture + QAbstractTexture::Target m_actualTarget; TextureProperties m_properties; TextureParameters m_parameters; diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp index 7703933a4..1bd002104 100644 --- a/src/render/texture/qabstracttexture.cpp +++ b/src/render/texture/qabstracttexture.cpp @@ -69,6 +69,11 @@ QAbstractTexturePrivate::QAbstractTexturePrivate() { } +QTextureGeneratorPtr QAbstractTexturePrivate::dataFunctor() const +{ + return m_dataFunctor; +} + void QAbstractTexturePrivate::setDataFunctor(const QTextureGeneratorPtr &generator) { if (generator != m_dataFunctor) { diff --git a/src/render/texture/qabstracttexture_p.h b/src/render/texture/qabstracttexture_p.h index 7f5a32c94..a27ae3729 100644 --- a/src/render/texture/qabstracttexture_p.h +++ b/src/render/texture/qabstracttexture_p.h @@ -56,12 +56,13 @@ #include <Qt3DRender/qabstracttexture.h> #include <Qt3DRender/qtexturewrapmode.h> #include <Qt3DRender/qtexturegenerator.h> +#include <Qt3DRender/private/qt3drender_global_p.h> QT_BEGIN_NAMESPACE namespace Qt3DRender { -class Q_AUTOTEST_EXPORT QAbstractTexturePrivate : public Qt3DCore::QNodePrivate +class QT3DRENDERSHARED_PRIVATE_EXPORT QAbstractTexturePrivate : public Qt3DCore::QNodePrivate { public : QAbstractTexturePrivate(); @@ -86,6 +87,7 @@ public : int m_layers; int m_samples; + QTextureGeneratorPtr dataFunctor() const; void setDataFunctor(const QTextureGeneratorPtr &generator); private: diff --git a/src/render/texture/qpaintedtextureimage.cpp b/src/render/texture/qpaintedtextureimage.cpp index 9ae43378a..4d03eb809 100644 --- a/src/render/texture/qpaintedtextureimage.cpp +++ b/src/render/texture/qpaintedtextureimage.cpp @@ -104,14 +104,9 @@ QPaintedTextureImage::~QPaintedTextureImage() /*! \property QPaintedTextureImage::width - Holds the width of the texture image - */ - -/*! - \property QPaintedTextureImage::width - - \return the width of the texture image. - */ + This property holds the width of the texture image. + The width must be greater than or equal to 1. +*/ int QPaintedTextureImage::width() const { Q_D(const QPaintedTextureImage); @@ -121,14 +116,9 @@ int QPaintedTextureImage::width() const /*! \property QPaintedTextureImage::height - Holds the height of the texture image - */ - -/*! - \property QPaintedTextureImage::height - - \return the height of the texture image. - */ + This property holds the height of the texture image. + The height must be greater than or equal to 1. +*/ int QPaintedTextureImage::height() const { Q_D(const QPaintedTextureImage); @@ -138,14 +128,11 @@ int QPaintedTextureImage::height() const /*! \property QPaintedTextureImage::size - Holds the width and height of the texture image - */ + This property holds the size of the texture image. -/*! - \property QPaintedTextureImage::size + \sa height, width - \return the size of the texture image. - */ +*/ QSize QPaintedTextureImage::size() const { Q_D(const QPaintedTextureImage); @@ -153,7 +140,7 @@ QSize QPaintedTextureImage::size() const } /*! - Sets the width of the texture image. Triggers an update, if the size changes. + Sets the width (\a w) of the texture image. Triggers an update, if the size changes. */ void QPaintedTextureImage::setWidth(int w) { @@ -165,7 +152,7 @@ void QPaintedTextureImage::setWidth(int w) } /*! - Sets the height of the texture image. Triggers an update, if the size changes. + Sets the height (\a h) of the texture image. Triggers an update, if the size changes. */ void QPaintedTextureImage::setHeight(int h) { @@ -177,7 +164,7 @@ void QPaintedTextureImage::setHeight(int h) } /*! - Sets the width and height of the texture image. Triggers an update, if the size changes. + Sets the width and height of the texture image. Triggers an update, if the \a size changes. */ void QPaintedTextureImage::setSize(QSize size) { @@ -206,9 +193,10 @@ void QPaintedTextureImage::setSize(QSize size) } /*! - Will trigger an update of the texture image, meaning the paint() method will - be invoked. - */ + Schedules the painted texture's paint() function to be called, + which in turn uploads the new image to the GPU. + Parameter \a rect is currently unused. +*/ void QPaintedTextureImage::update(const QRect &rect) { Q_UNUSED(rect) diff --git a/src/render/texture/qtexture.cpp b/src/render/texture/qtexture.cpp index 7dc071833..931d66c64 100644 --- a/src/render/texture/qtexture.cpp +++ b/src/render/texture/qtexture.cpp @@ -44,7 +44,17 @@ #include "qtexture.h" #include "qtexture_p.h" #include <QFileInfo> +#include <QMimeDatabase> +#include <QMimeType> #include <qendian.h> +#include <Qt3DCore/private/qscene_p.h> +#include <Qt3DCore/qaspectengine.h> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> +#include <Qt3DRender/private/qrenderaspect_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/texture_p.h> +#include <Qt3DRender/private/qurlhelper_p.h> QT_BEGIN_NAMESPACE @@ -390,7 +400,7 @@ const struct DX10Format { DXGI_FORMAT_R8_UNORM, { QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, 1, false } }, { DXGI_FORMAT_R8G8_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG8_UNorm, QOpenGLTexture::UInt8, 2, false } }, { DXGI_FORMAT_R16_UNORM, { QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, 2, false } }, -{ DXGI_FORMAT_R16_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG16_UNorm, QOpenGLTexture::UInt16, 4, false } }, +{ DXGI_FORMAT_R16G16_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG16_UNorm, QOpenGLTexture::UInt16, 4, false } }, // depth formats { DXGI_FORMAT_D16_UNORM, { QOpenGLTexture::Depth, QOpenGLTexture::D16, QOpenGLTexture::NoPixelType, 2, false } }, @@ -424,9 +434,8 @@ enum CompressedFormatExtension { PKM }; -CompressedFormatExtension texturedCompressedFormat(const QString &source) +CompressedFormatExtension texturedCompressedFormat(const QString &suffix) { - const QString suffix = QFileInfo(source).suffix(); if (suffix == QStringLiteral("pkm")) return PKM; if (suffix == QStringLiteral("dds")) @@ -434,19 +443,14 @@ CompressedFormatExtension texturedCompressedFormat(const QString &source) return None; } -QTextureImageDataPtr setPkmFile(const QString &source) +QTextureImageDataPtr setPkmFile(QIODevice *source) { QTextureImageDataPtr imageData; - QFile f(source); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << source; - return imageData; - } - // ETC1 in PKM, as generated by f.ex. Android's etc1tool + // ETC1 in PKM, as generated by source->ex. Android's etc1tool static const char pkmMagic[] = { 'P', 'K', 'M', ' ', '1', '0' }; const int pkmHeaderSize = 6 + 2 + 4 * 2; - const QByteArray header = f.read(pkmHeaderSize); + const QByteArray header = source->read(pkmHeaderSize); if (header.size() >= pkmHeaderSize && !qstrncmp(header.constData(), pkmMagic, 6)) { imageData = QTextureImageDataPtr::create(); imageData->setTarget(QOpenGLTexture::Target2D); @@ -455,7 +459,7 @@ QTextureImageDataPtr setPkmFile(const QString &source) imageData->setWidth(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2)))); imageData->setHeight(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2 + 2)))); imageData->setDepth(1); - const QByteArray data = f.readAll(); + const QByteArray data = source->readAll(); if (data.size() < (imageData->width() / 4) * (imageData->height() / 4) * 8) qWarning() << "Unexpected end of ETC1 data in" << source; const bool isCompressed = true; @@ -467,17 +471,12 @@ QTextureImageDataPtr setPkmFile(const QString &source) return imageData; } -QTextureImageDataPtr setDdsFile(const QString &source) +QTextureImageDataPtr setDdsFile(QIODevice *source) { QTextureImageDataPtr imageData; - QFile f(source); - if (!f.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << source; - return imageData; - } DdsHeader header; - if ((f.read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header) + if ((source->read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header) || (qstrncmp(header.magic, "DDS ", 4) != 0)) return imageData; @@ -490,7 +489,7 @@ QTextureImageDataPtr setDdsFile(const QString &source) if (fourCC == DdsFourCC<'D', 'X', '1', '0'>::value) { // DX10 texture DdsDX10Header dx10Header; - if (f.read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header) + if (source->read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header) return imageData; layers = qFromLittleEndian(dx10Header.arraySize); @@ -582,13 +581,13 @@ QTextureImageDataPtr setDdsFile(const QString &source) // data const int dataSize = layers * layerSize; - const QByteArray data = f.read(dataSize); + const QByteArray data = source->read(dataSize); if (data.size() < dataSize) { qWarning() << "Unexpected end of data in" << source; return imageData; } - if (!f.atEnd()) + if (!source->atEnd()) qWarning() << "Unrecognized data in" << source; imageData = QTextureImageDataPtr::create(); @@ -626,26 +625,39 @@ QTextureImageDataPtr TextureLoadingHelper::loadTextureData(const QUrl &url, bool #endif ) { const QString source = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(url); - const CompressedFormatExtension formatExtension = texturedCompressedFormat(source); - switch (formatExtension) { - case DDS: - textureData = setDdsFile(source); - break; - case PKM: - textureData = setPkmFile(source); - break; - default: - QImage img; - if (img.load(source)) { - textureData = QTextureImageDataPtr::create(); - textureData->setImage(mirrored ? img.mirrored() : img); - } - break; - } + QFile f(source); + if (!f.open(QIODevice::ReadOnly)) + qWarning() << "Failed to open" << source; + else + textureData = loadTextureData(&f, QFileInfo(source).suffix(), allow3D, mirrored); + } + return textureData; +} - if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1)) - qWarning() << "Texture data has a 3rd dimension which wasn't expected"; +QTextureImageDataPtr TextureLoadingHelper::loadTextureData(QIODevice *data, const QString& suffix, + bool allow3D, bool mirrored) +{ + QTextureImageDataPtr textureData; + const CompressedFormatExtension formatExtension = texturedCompressedFormat(suffix); + switch (formatExtension) { + case DDS: + textureData = setDdsFile(data); + break; + case PKM: + textureData = setPkmFile(data); + break; + default: { + QImage img; + if (img.load(data, suffix.toLatin1())) { + textureData = QTextureImageDataPtr::create(); + textureData->setImage(mirrored ? img.mirrored() : img); + } + break; + } } + + if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1)) + qWarning() << "Texture data has a 3rd dimension which wasn't expected"; return textureData; } @@ -653,8 +665,43 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()() { QTextureDataPtr generatedData = QTextureDataPtr::create(); m_status = QAbstractTexture::Loading; + QTextureImageDataPtr textureData; + + if (!Qt3DCore::QDownloadHelperService::isLocal(m_url)) { + if (m_sourceData.isEmpty()) { + // first time around, trigger a download + if (m_texture) { + auto downloadService = Qt3DCore::QDownloadHelperService::getService(m_engine); + Qt3DCore::QDownloadRequestPtr request(new TextureDownloadRequest(m_texture, m_url, + m_engine)); + downloadService->submitRequest(request); + } + return generatedData; + } + + // second time around, we have the data + QT_PREPEND_NAMESPACE(QBuffer) buffer(&m_sourceData); + if (buffer.open(QIODevice::ReadOnly)) { + QString suffix = m_url.toString(); + suffix = suffix.right(suffix.length() - suffix.lastIndexOf('.')); + + QStringList ext(suffix); + + QMimeDatabase db; + QMimeType mtype = db.mimeTypeForData(m_sourceData); + if (mtype.isValid()) { + ext << mtype.suffixes(); + } - const QTextureImageDataPtr textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored); + for (QString s: qAsConst(ext)) { + textureData = TextureLoadingHelper::loadTextureData(&buffer, suffix, true, m_mirrored); + if (textureData && textureData->data().length() > 0) + break; + } + } + } else { + textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored); + } if (textureData && textureData->data().length() > 0) { generatedData->setTarget(static_cast<QAbstractTexture::Target>(textureData->target())); @@ -672,12 +719,55 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()() return generatedData; } +TextureDownloadRequest::TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl& source, + Qt3DCore::QAspectEngine *engine) + : Qt3DCore::QDownloadRequest(source) + , m_texture(texture) + , m_engine(engine) +{ + +} + +// Executed in download thread +void TextureDownloadRequest::onCompleted() +{ + if (cancelled() || !succeeded()) + return; + + QRenderAspectPrivate* d_aspect = QRenderAspectPrivate::findPrivate(m_engine); + if (!d_aspect) + return; + + Render::Texture *texture = d_aspect->m_nodeManagers->textureManager()->lookupResource(m_texture); + if (!texture) + return; + + QSharedPointer<QTextureFromSourceGenerator> functor = + qSharedPointerCast<QTextureFromSourceGenerator>(texture->dataGenerator()); + functor->m_sourceData = m_data; + + // mark the component as dirty so that the functor runs again in the correct job + texture->addDirtyFlag(Render::Texture::DirtyDataGenerator); +} + QTextureLoaderPrivate::QTextureLoaderPrivate() : QAbstractTexturePrivate() , m_mirrored(true) { } +void QTextureLoaderPrivate::setScene(Qt3DCore::QScene *scene) +{ + QAbstractTexturePrivate::setScene(scene); + updateFunctor(); +} + +void QTextureLoaderPrivate::updateFunctor() +{ + Qt3DCore::QAspectEngine *engine = m_scene ? m_scene->engine() : nullptr; + setDataFunctor(QTextureFromSourceGeneratorPtr::create(m_id, m_source, m_mirrored, engine)); +} + /*! \class Qt3DRender::QTexture1D \inmodule Qt3DRender @@ -900,10 +990,25 @@ QTextureBuffer::~QTextureBuffer() /*! * Constructs a new Qt3DRender::QTextureLoader instance with \a parent as parent. + * + * Note that by default, if not contradicted by the file metadata, the loaded texture + * will have the following properties set: + * - wrapMode set to Repeat + * - minificationFilter set to LinearMipMapLinear + * - magnificationFilter set to Linear + * - generateMipMaps set to true + * - maximumAnisotropy set to 16.0f + * - target set to TargetAutomatic */ QTextureLoader::QTextureLoader(QNode *parent) : QAbstractTexture(*new QTextureLoaderPrivate, parent) { + d_func()->m_wrapMode.setX(QTextureWrapMode::Repeat); + d_func()->m_wrapMode.setY(QTextureWrapMode::Repeat); + d_func()->m_minFilter = LinearMipMapLinear; + d_func()->m_magFilter = Linear; + d_func()->m_autoMipMap = true; + d_func()->m_maximumAnisotropy = 16.0f; d_func()->m_target = TargetAutomatic; } @@ -936,7 +1041,7 @@ void QTextureLoader::setSource(const QUrl& source) Q_D(QTextureLoader); if (source != d->m_source) { d->m_source = source; - d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored)); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit sourceChanged(source); blockNotifications(blocked); @@ -984,7 +1089,7 @@ void QTextureLoader::setMirrored(bool mirrored) Q_D(QTextureLoader); if (mirrored != d->m_mirrored) { d->m_mirrored = mirrored; - d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored)); + d->updateFunctor(); const bool blocked = blockNotifications(true); emit mirroredChanged(mirrored); blockNotifications(blocked); @@ -1000,7 +1105,8 @@ bool QTextureFromSourceGenerator::operator ==(const QTextureGenerator &other) co const QTextureFromSourceGenerator *otherFunctor = functor_cast<QTextureFromSourceGenerator>(&other); return (otherFunctor != nullptr && otherFunctor->m_url == m_url && - otherFunctor->m_mirrored == m_mirrored); + otherFunctor->m_mirrored == m_mirrored && + otherFunctor->m_engine == m_engine); } QUrl QTextureFromSourceGenerator::url() const @@ -1018,16 +1124,18 @@ bool QTextureFromSourceGenerator::isMirrored() const * instance with \a url. * \param url */ -QTextureFromSourceGenerator::QTextureFromSourceGenerator(const QUrl &url, bool mirrored) +QTextureFromSourceGenerator::QTextureFromSourceGenerator(Qt3DCore::QNodeId texture, + const QUrl &url, bool mirrored, + Qt3DCore::QAspectEngine *engine) : QTextureGenerator() , m_url(url) , m_status(QAbstractTexture::None) , m_mirrored(mirrored) + , m_texture(texture) + , m_engine(engine) { } } // namespace Qt3DRender QT_END_NAMESPACE - - diff --git a/src/render/texture/qtexture_p.h b/src/render/texture/qtexture_p.h index a0ea71a58..4afb14d62 100644 --- a/src/render/texture/qtexture_p.h +++ b/src/render/texture/qtexture_p.h @@ -51,8 +51,11 @@ // We mean it. // +#include <Qt3DCore/QNodeId> +#include <Qt3DCore/private/qdownloadhelperservice_p.h> #include <Qt3DRender/private/qabstracttexture_p.h> #include <Qt3DRender/qtexturegenerator.h> +#include <Qt3DRender/qtexture.h> QT_BEGIN_NAMESPACE @@ -63,14 +66,30 @@ class QTextureLoaderPrivate : public QAbstractTexturePrivate public: QTextureLoaderPrivate(); + void setScene(Qt3DCore::QScene *scene) override; + void updateFunctor(); + QUrl m_source; bool m_mirrored; }; +class Q_AUTOTEST_EXPORT TextureDownloadRequest : public Qt3DCore::QDownloadRequest +{ +public: + TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl &url, Qt3DCore::QAspectEngine *engine); + + void onCompleted() Q_DECL_OVERRIDE; + +private: + Qt3DCore::QNodeId m_texture; + Qt3DCore::QAspectEngine *m_engine; +}; + class Q_AUTOTEST_EXPORT QTextureFromSourceGenerator : public QTextureGenerator { public: - explicit QTextureFromSourceGenerator(const QUrl &url, bool mirrored); + explicit QTextureFromSourceGenerator(Qt3DCore::QNodeId texture, const QUrl &url, + bool mirrored, Qt3DCore::QAspectEngine *engine); QTextureDataPtr operator ()() Q_DECL_OVERRIDE; bool operator ==(const QTextureGenerator &other) const Q_DECL_OVERRIDE; inline QAbstractTexture::Status status() const { return m_status; } @@ -81,9 +100,14 @@ public: bool isMirrored() const; private: + friend class TextureDownloadRequest; + QUrl m_url; QAbstractTexture::Status m_status; bool m_mirrored; + QByteArray m_sourceData; + Qt3DCore::QNodeId m_texture; + Qt3DCore::QAspectEngine *m_engine; }; typedef QSharedPointer<QTextureFromSourceGenerator> QTextureFromSourceGeneratorPtr; @@ -91,6 +115,8 @@ class Q_AUTOTEST_EXPORT TextureLoadingHelper { public: static QTextureImageDataPtr loadTextureData(const QUrl &source, bool allow3D, bool mirrored); + static QTextureImageDataPtr loadTextureData(QIODevice *data, const QString& suffix, + bool allow3D, bool mirrored); }; } // namespace Qt3DRender diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp index 863733523..21f29d0b6 100644 --- a/src/render/texture/texture.cpp +++ b/src/render/texture/texture.cpp @@ -61,7 +61,7 @@ namespace Render { Texture::Texture() // We need backend -> frontend notifications to update the status of the texture : BackendNode(ReadWrite) - , m_dirty(DirtyGenerators|DirtyProperties|DirtyParameters) + , m_dirty(DirtyImageGenerators|DirtyProperties|DirtyParameters|DirtyDataGenerator) , m_textureImageManager(nullptr) { } @@ -81,11 +81,19 @@ void Texture::setTextureImageManager(TextureImageManager *manager) void Texture::addDirtyFlag(DirtyFlags flags) { + QMutexLocker lock(&m_flagsMutex); m_dirty |= flags; } +Texture::DirtyFlags Texture::dirtyFlags() +{ + QMutexLocker lock(&m_flagsMutex); + return m_dirty; +} + void Texture::unsetDirty() { + QMutexLocker lock(&m_flagsMutex); m_dirty = Texture::NotDirty; } @@ -101,7 +109,7 @@ void Texture::addTextureImage(Qt3DCore::QNodeId id) qWarning() << "[Qt3DRender::TextureNode] addTextureImage: image handle is NULL"; } else if (!m_textureImages.contains(handle)) { m_textureImages << handle; - addDirtyFlag(DirtyGenerators); + addDirtyFlag(DirtyImageGenerators); } } @@ -117,7 +125,7 @@ void Texture::removeTextureImage(Qt3DCore::QNodeId id) qWarning() << "[Qt3DRender::TextureNode] removeTextureImage: image handle is NULL"; } else { m_textureImages.removeAll(handle); - addDirtyFlag(DirtyGenerators); + addDirtyFlag(DirtyImageGenerators); } } @@ -214,7 +222,7 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) dirty = DirtyProperties; } else if (propertyChange->propertyName() == QByteArrayLiteral("generator")) { m_dataFunctor = propertyChange->value().value<QTextureGeneratorPtr>(); - dirty = DirtyGenerators; + dirty = DirtyDataGenerator; } } break; @@ -246,6 +254,16 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) BackendNode::sceneChangeEvent(e); } +bool Texture::isValid() const +{ + for (const auto handle : m_textureImages) { + TextureImage *img = m_textureImageManager->data(handle); + if (img == nullptr) + return false; + } + return true; +} + void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) { const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QAbstractTextureData>>(change); @@ -269,7 +287,7 @@ void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chan m_parameters.comparisonMode = data.comparisonMode; m_dataFunctor = data.dataFunctor; - addDirtyFlag(DirtyFlags(DirtyGenerators|DirtyProperties|DirtyParameters)); + addDirtyFlag(DirtyFlags(DirtyImageGenerators|DirtyProperties|DirtyParameters)); } diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h index 8f5300552..1f3ba729c 100644 --- a/src/render/texture/texture_p.h +++ b/src/render/texture/texture_p.h @@ -135,14 +135,15 @@ public: NotDirty = 0, DirtyProperties = 0x1, DirtyParameters = 0x2, - DirtyGenerators = 0x4 + DirtyImageGenerators = 0x4, + DirtyDataGenerator = 0x8 }; Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag) void setTextureImageManager(TextureImageManager *manager); void addDirtyFlag(DirtyFlags flags); - inline DirtyFlags dirtyFlags() const { return m_dirty; } + DirtyFlags dirtyFlags(); void unsetDirty(); void addTextureImage(Qt3DCore::QNodeId id); @@ -156,6 +157,7 @@ public: inline const QVector<HTextureImage>& textureImages() const { return m_textureImages; } inline const QTextureGeneratorPtr& dataGenerator() const { return m_dataFunctor; } + bool isValid() const; private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; @@ -167,6 +169,7 @@ private: QVector<HTextureImage> m_textureImages; TextureImageManager *m_textureImageManager; + QMutex m_flagsMutex; }; class TextureFunctor : public Qt3DCore::QBackendNodeMapper diff --git a/src/render/texture/textureimage.cpp b/src/render/texture/textureimage.cpp index f44a82649..b732be2d9 100644 --- a/src/render/texture/textureimage.cpp +++ b/src/render/texture/textureimage.cpp @@ -106,7 +106,7 @@ void TextureImage::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) // 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::DirtyGenerators); + txt->addDirtyFlag(Texture::DirtyImageGenerators); } markDirty(AbstractRenderer::AllDirty); |