1 | // Copyright (C) 2019 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 "texturesubmissioncontext_p.h" |
5 | |
6 | #include <graphicscontext_p.h> |
7 | #include <gltexture_p.h> |
8 | #include <logging_p.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | namespace Qt3DRender { |
13 | namespace Render { |
14 | namespace OpenGL { |
15 | |
16 | class TextureExtRendererLocker |
17 | { |
18 | public: |
19 | static void lock(GLTexture *tex) |
20 | { |
21 | if (!tex->isExternalRenderingEnabled()) |
22 | return; |
23 | if (s_lockHash.keys().contains(t: tex)) { |
24 | ++s_lockHash[tex]; |
25 | } else { |
26 | tex->externalRenderingLock()->lock(); |
27 | s_lockHash[tex] = 1; |
28 | } |
29 | } |
30 | static void unlock(GLTexture *tex) |
31 | { |
32 | if (!tex->isExternalRenderingEnabled()) |
33 | return; |
34 | if (!s_lockHash.keys().contains(t: tex)) |
35 | return; |
36 | |
37 | --s_lockHash[tex]; |
38 | if (s_lockHash[tex] == 0) { |
39 | s_lockHash.remove(key: tex); |
40 | tex->externalRenderingLock()->unlock(); |
41 | } |
42 | } |
43 | private: |
44 | static QHash<GLTexture*, int> s_lockHash; |
45 | }; |
46 | |
47 | QHash<GLTexture*, int> TextureExtRendererLocker::s_lockHash = QHash<GLTexture*, int>(); |
48 | |
49 | |
50 | TextureSubmissionContext::TextureSubmissionContext() |
51 | { |
52 | |
53 | } |
54 | |
55 | TextureSubmissionContext::~TextureSubmissionContext() |
56 | { |
57 | |
58 | } |
59 | |
60 | void TextureSubmissionContext::initialize(GraphicsContext *context) |
61 | { |
62 | m_activeTextures.resize(new_size: context->maxTextureUnitsCount()); |
63 | } |
64 | |
65 | void TextureSubmissionContext::endDrawing() |
66 | { |
67 | decayTextureScores(); |
68 | for (size_t i = 0; i < m_activeTextures.size(); ++i) |
69 | if (m_activeTextures[i].texture) |
70 | TextureExtRendererLocker::unlock(tex: m_activeTextures[i].texture); |
71 | } |
72 | |
73 | int TextureSubmissionContext::activateTexture(TextureSubmissionContext::TextureScope scope, |
74 | QOpenGLContext *m_gl, |
75 | GLTexture *tex) |
76 | { |
77 | // Returns the texture unit to use for the texture |
78 | // This always return a valid unit, unless there are more textures than |
79 | // texture unit available for the current material |
80 | const int onUnit = assignUnitForTexture(tex); |
81 | |
82 | // check we didn't overflow the available units |
83 | if (onUnit == -1) |
84 | return -1; |
85 | |
86 | const int sharedTextureId = tex->sharedTextureId(); |
87 | // We have a valid texture id provided by a shared context |
88 | if (sharedTextureId > 0) { |
89 | m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit); |
90 | const QAbstractTexture::Target target = tex->properties().target; |
91 | // For now we know that target values correspond to the GL values |
92 | m_gl->functions()->glBindTexture(target, texture: tex->sharedTextureId()); |
93 | } else { |
94 | // Texture must have been created and updated at this point |
95 | QOpenGLTexture *glTex = tex->getGLTexture(); |
96 | if (glTex == nullptr) |
97 | return -1; |
98 | glTex->bind(unit: uint(onUnit)); |
99 | } |
100 | if (m_activeTextures[onUnit].texture != tex) { |
101 | if (m_activeTextures[onUnit].texture) |
102 | TextureExtRendererLocker::unlock(tex: m_activeTextures[onUnit].texture); |
103 | m_activeTextures[onUnit].texture = tex; |
104 | TextureExtRendererLocker::lock(tex); |
105 | } |
106 | |
107 | #if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) |
108 | int err = m_gl->functions()->glGetError(); |
109 | if (err) |
110 | qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) |
111 | << tex->getGLTexture()->textureId() << "on unit" << onUnit; |
112 | #endif |
113 | |
114 | m_activeTextures[onUnit].score = 200; |
115 | m_activeTextures[onUnit].pinned = true; |
116 | m_activeTextures[onUnit].scope = scope; |
117 | |
118 | return onUnit; |
119 | } |
120 | |
121 | void TextureSubmissionContext::deactivateTexturesWithScope(TextureSubmissionContext::TextureScope ts) |
122 | { |
123 | for (size_t u=0; u<m_activeTextures.size(); ++u) { |
124 | if (!m_activeTextures[u].pinned) |
125 | continue; // inactive, ignore |
126 | |
127 | if (m_activeTextures[u].scope == ts) { |
128 | m_activeTextures[u].pinned = false; |
129 | m_activeTextures[u].score = qMax(a: m_activeTextures[u].score, b: 1) - 1; |
130 | } |
131 | } // of units iteration |
132 | } |
133 | |
134 | void TextureSubmissionContext::deactivateTexture(GLTexture* tex) |
135 | { |
136 | for (size_t u=0; u<m_activeTextures.size(); ++u) { |
137 | if (m_activeTextures[u].texture == tex) { |
138 | Q_ASSERT(m_activeTextures[u].pinned); |
139 | m_activeTextures[u].pinned = false; |
140 | return; |
141 | } |
142 | } // of units iteration |
143 | |
144 | qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; |
145 | } |
146 | |
147 | /*! |
148 | \internal |
149 | Returns a texture unit for a texture, -1 if all texture units are assigned. |
150 | Tries to use the texture unit with the texture that hasn't been used for the longest time |
151 | if the texture happens not to be already pinned on a texture unit. |
152 | */ |
153 | int TextureSubmissionContext::assignUnitForTexture(GLTexture *tex) |
154 | { |
155 | int lowestScoredUnit = -1; |
156 | int lowestScore = 0xfffffff; |
157 | |
158 | for (size_t u=0; u<m_activeTextures.size(); ++u) { |
159 | if (m_activeTextures[u].texture == tex) |
160 | return int(u); |
161 | } |
162 | |
163 | for (size_t u=0; u<m_activeTextures.size(); ++u) { |
164 | // No texture is currently active on the texture unit |
165 | // we save the texture unit with the texture that has been on there |
166 | // the longest time while not being used |
167 | if (!m_activeTextures[u].pinned) { |
168 | int score = m_activeTextures[u].score; |
169 | if (score < lowestScore) { |
170 | lowestScore = score; |
171 | lowestScoredUnit = int(u); |
172 | } |
173 | } |
174 | } // of units iteration |
175 | |
176 | if (lowestScoredUnit == -1) |
177 | qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!" ; |
178 | |
179 | return lowestScoredUnit; |
180 | } |
181 | |
182 | void TextureSubmissionContext::decayTextureScores() |
183 | { |
184 | for (size_t u = 0; u < m_activeTextures.size(); u++) |
185 | m_activeTextures[u].score = qMax(a: m_activeTextures[u].score - 1, b: 0); |
186 | } |
187 | |
188 | } // namespace OpenGL |
189 | } // namespace Render |
190 | } // namespace Qt3DRender of namespace |
191 | |
192 | QT_END_NAMESPACE |
193 | |