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 | using namespace Qt3DCore; |
17 | |
18 | namespace Qt3DRender { |
19 | namespace Render { |
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 |