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
10QT_BEGIN_NAMESPACE
11
12namespace Qt3DRender {
13namespace Render {
14namespace OpenGL {
15
16class TextureExtRendererLocker
17{
18public:
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 }
43private:
44 static QHash<GLTexture*, int> s_lockHash;
45};
46
47QHash<GLTexture*, int> TextureExtRendererLocker::s_lockHash = QHash<GLTexture*, int>();
48
49
50TextureSubmissionContext::TextureSubmissionContext()
51{
52
53}
54
55TextureSubmissionContext::~TextureSubmissionContext()
56{
57
58}
59
60void TextureSubmissionContext::initialize(GraphicsContext *context)
61{
62 m_activeTextures.resize(new_size: context->maxTextureUnitsCount());
63}
64
65void 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
73int 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
121void 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
134void 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 */
153int 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
182void 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
192QT_END_NAMESPACE
193

source code of qt3d/src/plugins/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp