| 1 | // Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). |
|---|---|
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "texture_p.h" |
| 5 | |
| 6 | #include <QDebug> |
| 7 | #include <QOpenGLFunctions> |
| 8 | #include <QOpenGLTexture> |
| 9 | |
| 10 | #include <Qt3DRender/private/texture_p.h> |
| 11 | #include <Qt3DRender/private/qabstracttexture_p.h> |
| 12 | #include <Qt3DRender/private/managers_p.h> |
| 13 | |
| 14 | QT_BEGIN_NAMESPACE |
| 15 | |
| 16 | namespace Qt3DRender { |
| 17 | namespace Render { |
| 18 | |
| 19 | using namespace Qt3DCore; |
| 20 | |
| 21 | Texture::Texture() |
| 22 | // We need backend -> frontend notifications to update the status of the texture |
| 23 | : BackendNode(ReadWrite) |
| 24 | , m_dirty(DirtyImageGenerators|DirtyProperties|DirtyParameters|DirtyDataGenerator) |
| 25 | , m_sharedTextureId(-1) |
| 26 | { |
| 27 | } |
| 28 | |
| 29 | Texture::~Texture() |
| 30 | { |
| 31 | // We do not abandon the api texture |
| 32 | // because if the dtor is called that means |
| 33 | // the manager was destroyed otherwise cleanup |
| 34 | // would have been called |
| 35 | } |
| 36 | |
| 37 | void Texture::addDirtyFlag(DirtyFlags flags) |
| 38 | { |
| 39 | QMutexLocker lock(&m_flagsMutex); |
| 40 | m_dirty |= flags; |
| 41 | if (m_renderer) |
| 42 | markDirty(changes: AbstractRenderer::TexturesDirty); |
| 43 | } |
| 44 | |
| 45 | Texture::DirtyFlags Texture::dirtyFlags() |
| 46 | { |
| 47 | QMutexLocker lock(&m_flagsMutex); |
| 48 | return m_dirty; |
| 49 | } |
| 50 | |
| 51 | void Texture::unsetDirty() |
| 52 | { |
| 53 | QMutexLocker lock(&m_flagsMutex); |
| 54 | m_dirty = Texture::NotDirty; |
| 55 | } |
| 56 | |
| 57 | // This is called by Renderer::updateGLResources |
| 58 | // when the texture has been marked for cleanup |
| 59 | void Texture::cleanup() |
| 60 | { |
| 61 | // Whoever calls this must make sure to also check if this |
| 62 | // texture is being referenced by a shared API specific texture (GLTexture) |
| 63 | m_dataFunctor.reset(); |
| 64 | m_textureImageIds.clear(); |
| 65 | m_pendingTextureDataUpdates.clear(); |
| 66 | m_sharedTextureId = -1; |
| 67 | |
| 68 | // set default values |
| 69 | m_properties = {}; |
| 70 | m_parameters = {}; |
| 71 | |
| 72 | m_dirty = NotDirty; |
| 73 | } |
| 74 | |
| 75 | void Texture::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) |
| 76 | { |
| 77 | BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
| 78 | const QAbstractTexture *node = qobject_cast<const QAbstractTexture *>(object: frontEnd); |
| 79 | if (!node) |
| 80 | return; |
| 81 | |
| 82 | TextureProperties p = m_properties; |
| 83 | p.width = node->width(); |
| 84 | p.height = node->height(); |
| 85 | p.depth = node->depth(); |
| 86 | p.format = node->format(); |
| 87 | p.target = node->target(); |
| 88 | p.generateMipMaps = node->generateMipMaps(); |
| 89 | p.layers = node->layers(); |
| 90 | p.samples = node->samples(); |
| 91 | p.mipLevels = node->mipLevels(); |
| 92 | if (p != m_properties) { |
| 93 | m_properties = p; |
| 94 | addDirtyFlag(flags: DirtyProperties); |
| 95 | } |
| 96 | |
| 97 | TextureParameters q = m_parameters; |
| 98 | q.magnificationFilter = node->magnificationFilter(); |
| 99 | q.minificationFilter = node->minificationFilter(); |
| 100 | q.wrapModeX = const_cast<QAbstractTexture *>(node)->wrapMode()->x(); |
| 101 | q.wrapModeY = const_cast<QAbstractTexture *>(node)->wrapMode()->y(); |
| 102 | q.wrapModeZ = const_cast<QAbstractTexture *>(node)->wrapMode()->z(); |
| 103 | q.maximumAnisotropy = node->maximumAnisotropy(); |
| 104 | q.comparisonFunction = node->comparisonFunction(); |
| 105 | q.comparisonMode = node->comparisonMode(); |
| 106 | if (q != m_parameters) { |
| 107 | m_parameters = q; |
| 108 | addDirtyFlag(flags: DirtyParameters); |
| 109 | } |
| 110 | |
| 111 | QAbstractTexturePrivate *dnode = static_cast<QAbstractTexturePrivate *>(QAbstractTexturePrivate::get(q: const_cast<QAbstractTexture *>(node))); |
| 112 | auto newGenerator = dnode->dataFunctor(); |
| 113 | if (newGenerator != m_dataFunctor) { |
| 114 | setDataGenerator(newGenerator); |
| 115 | QAbstractTexturePrivate *dTexture = static_cast<QAbstractTexturePrivate *>(QNodePrivate::get(q: const_cast<QNode *>(frontEnd))); |
| 116 | dTexture->setStatus(QAbstractTexture::Loading); |
| 117 | } |
| 118 | |
| 119 | if (dnode) { |
| 120 | for (const QTextureDataUpdate &pendingUpdate : dnode->m_pendingDataUpdates) |
| 121 | addTextureDataUpdate(update: pendingUpdate); |
| 122 | dnode->m_pendingDataUpdates.clear(); |
| 123 | |
| 124 | auto ids = Qt3DCore::qIdsForNodes(nodes: dnode->m_textureImages); |
| 125 | std::sort(first: std::begin(cont&: ids), last: std::end(cont&: ids)); |
| 126 | if (ids != m_textureImageIds) { |
| 127 | m_textureImageIds = ids; |
| 128 | addDirtyFlag(flags: DirtyImageGenerators); |
| 129 | } |
| 130 | |
| 131 | if (dnode->m_sharedTextureId != m_sharedTextureId) { |
| 132 | m_sharedTextureId = dnode->m_sharedTextureId; |
| 133 | addDirtyFlag(flags: DirtySharedTextureId); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | // Called by syncFromFrontend or TextureDownloadRequest (both in AspectThread context) |
| 139 | void Texture::setDataGenerator(const QTextureGeneratorPtr &generator) |
| 140 | { |
| 141 | m_dataFunctor = generator; |
| 142 | addDirtyFlag(flags: DirtyDataGenerator); |
| 143 | } |
| 144 | |
| 145 | bool Texture::isValid(TextureImageManager *manager) const |
| 146 | { |
| 147 | for (const QNodeId &id : m_textureImageIds) { |
| 148 | TextureImage *img = manager->lookupResource(id); |
| 149 | if (img == nullptr) |
| 150 | return false; |
| 151 | } |
| 152 | return true; |
| 153 | } |
| 154 | |
| 155 | void Texture::addTextureDataUpdate(const QTextureDataUpdate &update) |
| 156 | { |
| 157 | m_pendingTextureDataUpdates.push_back(x: update); |
| 158 | addDirtyFlag(flags: DirtyPendingDataUpdates); |
| 159 | } |
| 160 | |
| 161 | |
| 162 | TextureFunctor::TextureFunctor(AbstractRenderer *renderer, |
| 163 | TextureManager *textureNodeManager) |
| 164 | : m_renderer(renderer) |
| 165 | , m_textureNodeManager(textureNodeManager) |
| 166 | { |
| 167 | } |
| 168 | |
| 169 | Qt3DCore::QBackendNode *TextureFunctor::create(Qt3DCore::QNodeId id) const |
| 170 | { |
| 171 | Texture *backend = m_textureNodeManager->getOrCreateResource(id); |
| 172 | backend->setRenderer(m_renderer); |
| 173 | // Remove id from cleanupList if for some reason we were in the dirty list of texture |
| 174 | // (Can happen when a node destroyed is followed by a node created change |
| 175 | // in the same loop, when changing parent for instance) |
| 176 | m_textureNodeManager->removeTextureIdToCleanup(id); |
| 177 | return backend; |
| 178 | } |
| 179 | |
| 180 | Qt3DCore::QBackendNode *TextureFunctor::get(Qt3DCore::QNodeId id) const |
| 181 | { |
| 182 | return m_textureNodeManager->lookupResource(id); |
| 183 | } |
| 184 | |
| 185 | void TextureFunctor::destroy(Qt3DCore::QNodeId id) const |
| 186 | { |
| 187 | m_textureNodeManager->addTextureIdToCleanup(id); |
| 188 | // We add ourselves to the dirty list to tell the shared texture managers |
| 189 | // in the renderer that this texture has been destroyed |
| 190 | m_textureNodeManager->releaseResource(id); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | } // namespace Render |
| 195 | } // namespace Qt3DRender |
| 196 | |
| 197 | QT_END_NAMESPACE |
| 198 |
