1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "imagesubmissioncontext_p.h" |
41 | #include <Qt3DRender/private/shaderimage_p.h> |
42 | #include <Qt3DRender/qshaderimage.h> |
43 | #include <graphicscontext_p.h> |
44 | #include <gltexture_p.h> |
45 | #include <logging_p.h> |
46 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | // ES 3.1+ or GL 4.2+ |
50 | #ifndef GL_READ_ONLY |
51 | #define GL_READ_ONLY 0x88B8 |
52 | #endif |
53 | #ifndef GL_WRITE_ONLY |
54 | #define GL_WRITE_ONLY 0x88B9 |
55 | #endif |
56 | #ifndef GL_READ_WRITE |
57 | #define GL_READ_WRITE 0x88BA |
58 | #endif |
59 | |
60 | namespace Qt3DRender { |
61 | namespace Render { |
62 | namespace OpenGL { |
63 | |
64 | class GraphicsContext; |
65 | class GLTexture; |
66 | |
67 | namespace { |
68 | |
69 | GLenum glAccessEnumForShaderImageAccess(QShaderImage::Access access) |
70 | { |
71 | switch (access) { |
72 | case QShaderImage::ReadOnly: |
73 | return GL_READ_ONLY; |
74 | case QShaderImage::WriteOnly: |
75 | return GL_WRITE_ONLY; |
76 | case QShaderImage::ReadWrite: |
77 | default: |
78 | break; |
79 | } |
80 | return GL_READ_WRITE; |
81 | } |
82 | |
83 | GLenum glImageFormatToGL(QShaderImage::ImageFormat format) |
84 | { |
85 | // Right now we can abuse from the fact that the ImageFormat enum values |
86 | // have been assigned the same value as the GL enum |
87 | return GLenum(format); |
88 | } |
89 | |
90 | GLenum glImageFormatForShaderImageFormat(QShaderImage::ImageFormat format, |
91 | QAbstractTexture::TextureFormat textureFormat) |
92 | { |
93 | Q_ASSERT_X(format != QShaderImage::NoFormat, Q_FUNC_INFO, "Valid image format or Automatic expected" ); |
94 | |
95 | if (format != QShaderImage::Automatic) |
96 | return glImageFormatToGL(format); |
97 | |
98 | // Otherwise try to mind to best texture format |
99 | switch (textureFormat) { |
100 | case QAbstractTexture::R8_UNorm: |
101 | return glImageFormatToGL(format: QShaderImage::R8_UNorm); |
102 | case QAbstractTexture::RG8_UNorm: |
103 | return glImageFormatToGL(format: QShaderImage::RG8_UNorm); |
104 | case QAbstractTexture::RGBA8_UNorm: |
105 | return glImageFormatToGL(format: QShaderImage::RGBA8_UNorm); |
106 | |
107 | case QAbstractTexture::R16_UNorm: |
108 | return glImageFormatToGL(format: QShaderImage::R16_UNorm); |
109 | case QAbstractTexture::RG16_UNorm: |
110 | return glImageFormatToGL(format: QShaderImage::RG16_UNorm); |
111 | case QAbstractTexture::RGBA16_UNorm: |
112 | return glImageFormatToGL(format: QShaderImage::RGBA16_UNorm); |
113 | |
114 | case QAbstractTexture::R8_SNorm: |
115 | return glImageFormatToGL(format: QShaderImage::R8_SNorm); |
116 | case QAbstractTexture::RG8_SNorm: |
117 | return glImageFormatToGL(format: QShaderImage::RG8_SNorm); |
118 | case QAbstractTexture::RGBA8_SNorm: |
119 | return glImageFormatToGL(format: QShaderImage::RGBA8_SNorm); |
120 | |
121 | case QAbstractTexture::R16_SNorm: |
122 | return glImageFormatToGL(format: QShaderImage::R16_SNorm); |
123 | case QAbstractTexture::RG16_SNorm: |
124 | return glImageFormatToGL(format: QShaderImage::RG16_SNorm); |
125 | case QAbstractTexture::RGBA16_SNorm: |
126 | return glImageFormatToGL(format: QShaderImage::RGBA16_SNorm); |
127 | |
128 | case QAbstractTexture::R8U: |
129 | return glImageFormatToGL(format: QShaderImage::R8U); |
130 | case QAbstractTexture::RG8U: |
131 | return glImageFormatToGL(format: QShaderImage::RG8U); |
132 | case QAbstractTexture::RGBA8U: |
133 | return glImageFormatToGL(format: QShaderImage::RGBA8U); |
134 | |
135 | case QAbstractTexture::R16U: |
136 | return glImageFormatToGL(format: QShaderImage::R16U); |
137 | case QAbstractTexture::RG16U: |
138 | return glImageFormatToGL(format: QShaderImage::RG16U); |
139 | case QAbstractTexture::RGBA16U: |
140 | return glImageFormatToGL(format: QShaderImage::RGBA16U); |
141 | |
142 | case QAbstractTexture::R32U: |
143 | return glImageFormatToGL(format: QShaderImage::R32U); |
144 | case QAbstractTexture::RG32U: |
145 | return glImageFormatToGL(format: QShaderImage::RG32U); |
146 | case QAbstractTexture::RGBA32U: |
147 | return glImageFormatToGL(format: QShaderImage::RGBA32U); |
148 | |
149 | case QAbstractTexture::R8I: |
150 | return glImageFormatToGL(format: QShaderImage::R8I); |
151 | case QAbstractTexture::RG8I: |
152 | return glImageFormatToGL(format: QShaderImage::RG8I); |
153 | case QAbstractTexture::RGBA8I: |
154 | return glImageFormatToGL(format: QShaderImage::RGBA8I); |
155 | |
156 | case QAbstractTexture::R16I: |
157 | return glImageFormatToGL(format: QShaderImage::R16I); |
158 | case QAbstractTexture::RG16I: |
159 | return glImageFormatToGL(format: QShaderImage::RG16I); |
160 | case QAbstractTexture::RGBA16I: |
161 | return glImageFormatToGL(format: QShaderImage::RGBA16I); |
162 | |
163 | case QAbstractTexture::R32I: |
164 | return glImageFormatToGL(format: QShaderImage::R32I); |
165 | case QAbstractTexture::RG32I: |
166 | return glImageFormatToGL(format: QShaderImage::RG32I); |
167 | case QAbstractTexture::RGBA32I: |
168 | return glImageFormatToGL(format: QShaderImage::RGBA32I); |
169 | |
170 | case QAbstractTexture::R16F: |
171 | return glImageFormatToGL(format: QShaderImage::R16F); |
172 | case QAbstractTexture::RG16F: |
173 | return glImageFormatToGL(format: QShaderImage::RG16F); |
174 | case QAbstractTexture::RGBA16F: |
175 | return glImageFormatToGL(format: QShaderImage::RGBA16F); |
176 | |
177 | case QAbstractTexture::R32F: |
178 | return glImageFormatToGL(format: QShaderImage::R32F); |
179 | case QAbstractTexture::RG32F: |
180 | return glImageFormatToGL(format: QShaderImage::RG32F); |
181 | case QAbstractTexture::RGBA32F: |
182 | return glImageFormatToGL(format: QShaderImage::RGBA32F); |
183 | |
184 | case QAbstractTexture::RG11B10F: |
185 | return glImageFormatToGL(format: QShaderImage::RG11B10F); |
186 | case QAbstractTexture::RGB10A2: |
187 | return glImageFormatToGL(format: QShaderImage::RGB10A2); |
188 | case QAbstractTexture::RGB10A2U: |
189 | return glImageFormatToGL(format: QShaderImage::RGB10A2U); |
190 | |
191 | default: |
192 | qWarning() << "Cannot map Texture format" << textureFormat << "to a valid Image Format" ; |
193 | Q_UNREACHABLE(); |
194 | return GL_NONE; |
195 | } |
196 | } |
197 | |
198 | } // anonymous |
199 | |
200 | ImageSubmissionContext::ImageSubmissionContext() |
201 | : m_ctx(nullptr) |
202 | { |
203 | } |
204 | |
205 | void ImageSubmissionContext::initialize(GraphicsContext *context) |
206 | { |
207 | m_ctx = context; |
208 | m_activeImages.resize(asize: m_ctx->maxImageUnitsCount()); |
209 | } |
210 | |
211 | void ImageSubmissionContext::endDrawing() |
212 | { |
213 | // Reduce score of all active Images |
214 | decayImageScores(); |
215 | } |
216 | |
217 | // Return Image Unit for Image |
218 | // If Image was used previously and recently, it will return the last used unit |
219 | // for that image. Otherwise it will try to return the image unit the least used. |
220 | int ImageSubmissionContext::activateImage(ShaderImage *image, GLTexture *tex) |
221 | { |
222 | const int onUnit = assignUnitForImage(shaderImageId: image->peerId()); |
223 | |
224 | if (onUnit < 0) { |
225 | qWarning() << "Unable to find available image unit" ; |
226 | return -1; |
227 | } |
228 | |
229 | QOpenGLTexture *glTex = tex->getGLTexture(); |
230 | if (glTex == nullptr) { |
231 | qWarning() << "Unable to retrieve valid texture for Image" ; |
232 | return -1; |
233 | } |
234 | |
235 | // Bind Image against Texture and resolve Image Format |
236 | m_ctx->bindImageTexture(imageUnit: onUnit, |
237 | texture: glTex->textureId(), |
238 | mipLevel: image->mipLevel(), |
239 | layered: image->layered(), |
240 | layer: image->layer(), |
241 | access: glAccessEnumForShaderImageAccess(access: image->access()), |
242 | format: glImageFormatForShaderImageFormat(format: image->format(), |
243 | textureFormat: tex->properties().format)); |
244 | |
245 | // Store information about the Texture/Image on ActiveImage for given |
246 | // image unit |
247 | m_activeImages[onUnit].shaderImageId = image->peerId(); |
248 | m_activeImages[onUnit].texture = tex; |
249 | m_activeImages[onUnit].score = 200; |
250 | m_activeImages[onUnit].pinned = true; |
251 | |
252 | return onUnit; |
253 | } |
254 | |
255 | // Unset pinned Active Image and reduce their score |
256 | void ImageSubmissionContext::deactivateImages() |
257 | { |
258 | for (int u = 0, m = m_activeImages.size(); u < m; ++u) { |
259 | if (m_activeImages[u].pinned) { |
260 | m_activeImages[u].pinned = false; |
261 | m_activeImages[u].score = qMax(a: m_activeImages[u].score - 1, b: 0); |
262 | return; |
263 | } |
264 | } |
265 | } |
266 | |
267 | // Reduce score of all active images (pinned or not) |
268 | void ImageSubmissionContext::decayImageScores() |
269 | { |
270 | for (int u = 0, m = m_activeImages.size(); u < m; ++u) |
271 | m_activeImages[u].score = qMax(a: m_activeImages[u].score - 1, b: 0); |
272 | } |
273 | |
274 | int ImageSubmissionContext::assignUnitForImage(Qt3DCore::QNodeId shaderImageId) |
275 | { |
276 | int lowestScoredUnit = -1; |
277 | int lowestScore = 0xfffffff; |
278 | |
279 | const int m = m_activeImages.size(); |
280 | for (int u = 0; u < m; ++u) { |
281 | if (m_activeImages[u].shaderImageId == shaderImageId) |
282 | return u; |
283 | } |
284 | |
285 | for (int u = 0; u < m; ++u) { |
286 | // No image is currently active on the image unit |
287 | // we save the image unit with the texture that has been on there |
288 | // the longest time while not being used |
289 | if (!m_activeImages[u].pinned) { |
290 | const int score = m_activeImages[u].score; |
291 | if (score < lowestScore) { |
292 | lowestScore = score; |
293 | lowestScoredUnit = u; |
294 | } |
295 | } |
296 | } |
297 | |
298 | if (lowestScoredUnit == -1) |
299 | qCWarning(Backend) << Q_FUNC_INFO << "No free image units!" ; |
300 | |
301 | return lowestScoredUnit; |
302 | } |
303 | |
304 | } // namespace OpenGL |
305 | } // namespace Render |
306 | } // namespace Qt3DRender |
307 | |
308 | QT_END_NAMESPACE |
309 | |